//**********  pngwriter.cc   **********************************************
//  Author:                    Paul Blackburn
//
//  Email:                     individual61@users.sourceforge.net
//
//  Version:                   0.5.4   (19 / II / 2009)
//
//  Description:               Library that allows plotting a 48 bit
//                             PNG image pixel by pixel, which can
//                             then be opened with a graphics program.
//
//  License:                   GNU General Public License
//                             Copyright 2002, 2003, 2004, 2005, 2006, 2007, 
//                             2008, 2009 Paul Blackburn
//
//  Website: Main:             http://pngwriter.sourceforge.net/
//           Sourceforge.net:  http://sourceforge.net/projects/pngwriter/
//           Freshmeat.net:    http://freshmeat.net/projects/pngwriter/
//
//  Documentation:             The header file (pngwriter.h) is commented, but for a
//                             quick reference document, and support,
//                             take a look at the website.
//
//*************************************************************************

/*
 *     This program is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License as
 *     published by the Free Software Foundation; either version 2 of
 *     the License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public
 *     License along with this program; if not, write to the Free
 *     Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *     MA 02111-1307 USA
 *
 */

#include "pngwriter.hpp"

#include <map>
#include <algorithm>
#include <cmath>

const double pngwriter::pi_( acos( -1. ) );

// Default Constructor
////////////////////////////////////////////////////////////////////////////
pngwriter::pngwriter()
  : height_( 250 ), width_( 250 ), backgroundcolour_( 65535 ), bit_depth_( 16 ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( .5 ),
    screengamma_( 2.2 )
{
  filename_ = new char[255];
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_  = new char[255];
  textsoftware_ = new char[255];

  strcpy(filename_, "out.png");

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, "out.png");

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

//Copy Constructor
//////////////////////////////////////////////////////////////////////////
pngwriter::pngwriter(const pngwriter &rhs)
  : height_( rhs.height_ ), width_( rhs.width_ ), backgroundcolour_( rhs.backgroundcolour_ ),
    bit_depth_( rhs.bit_depth_ ), colortype_( rhs.colortype_ ),
    compressionlevel_( rhs.compressionlevel_ ), transformation_( rhs.transformation_ ),
    filegamma_( rhs.filegamma_ ), screengamma_( rhs.screengamma_ )
{
  filename_ = new char[strlen(rhs.filename_)+1];
  textauthor_ = new char[strlen(rhs.textauthor_)+1];
  textdescription_ = new char[strlen(rhs.textdescription_)+1];
  textsoftware_ = new char[strlen(rhs.textsoftware_)+1];
  texttitle_ = new char[strlen(rhs.texttitle_)+1];

  strcpy(filename_, rhs.filename_);
  strcpy(textauthor_, rhs.textauthor_);
  strcpy(textdescription_, rhs.textdescription_);
  strcpy(textsoftware_,rhs.textsoftware_);
  strcpy(texttitle_, rhs.texttitle_);

  if ( init_pixel_matrix( rhs.graph_ ) > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

//Constructor for int colour levels, char * filename
//////////////////////////////////////////////////////////////////////////
pngwriter::pngwriter(const unsigned & x, const unsigned & y,
		     const pngwriter::Byte & backgroundcolour,
		     char * filename, const int & depth)
  : height_( y ), width_( x ), backgroundcolour_( int( backgroundcolour ) << 8 ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( 0.6 ),
    screengamma_( 2.2 )
{
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_ = new char[strlen(filename)+1];
  textsoftware_ = new char[255];
  filename_ = new char[strlen(filename)+1];

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, filename);
  strcpy(filename_, filename);

  setbitdepth( depth );

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

pngwriter::pngwriter(const unsigned & x, const unsigned & y, const int & backgroundcolour,
		     char * filename, const int & depth)
  : height_( y ), width_( x ),
    backgroundcolour_( std::max( 0, std::min( 65535, backgroundcolour ) ) ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( 0.6 ),
    screengamma_( 2.2 ) 
{
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_ = new char[strlen(filename)+1];
  textsoftware_ = new char[255];
  filename_ = new char[strlen(filename)+1];

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, filename);
  strcpy(filename_, filename);

  setbitdepth( depth );

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

//Constructor for double levels, char * filename
/////////////////////////////////////////////////////////////////////////
pngwriter::pngwriter(const unsigned & x, const unsigned & y,
		     const double & backgroundcolour,
		     char * filename, const int & depth)
  : height_( y ), width_( x ),
    backgroundcolour_( int( std::max( 0., std::min( backgroundcolour, 1. ) ) * 65535. ) ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( 0.6 ),
    screengamma_( 2.2 ) 
{
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_ = new char[strlen(filename)+1];
  textsoftware_ = new char[255];
  filename_ = new char[strlen(filename)+1];

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, filename);
  strcpy(filename_, filename);

  setbitdepth( depth );

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

//Destructor
///////////////////////////////////////
pngwriter::~pngwriter()
{
  delete [] filename_;
  delete [] textauthor_;
  delete [] textdescription_;
  delete [] texttitle_;
  delete [] textsoftware_;

  for (unsigned jjj( 0 ); jjj < height_; jjj++) free(graph_[jjj]);
  free(graph_);
}

//Constructor for int levels, const char * filename
//////////////////////////////////////////////////////////////
pngwriter::pngwriter(const unsigned & x, const unsigned & y,
		     const pngwriter::Byte & backgroundcolour,
		     const char * filename, const int & depth)
  : height_( y ), width_( x ), backgroundcolour_( int( backgroundcolour ) << 8 ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( 0.6 ),
    screengamma_( 2.2 ) 
{
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_ = new char[strlen(filename)+1];
  textsoftware_ = new char[255];
  filename_ = new char[strlen(filename)+1];

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, filename);
  strcpy(filename_, filename);

  setbitdepth( depth );
  colortype_ =  2;

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

pngwriter::pngwriter(const unsigned & x, const unsigned & y,
		     const int & backgroundcolour,
		     const char * filename, const int & depth)
  : height_( y ), width_( x ),
    backgroundcolour_(  std::max( 0, std::min( backgroundcolour, 65535 ) ) ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( 0.6 ),
    screengamma_( 2.2 ) 
{
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_ = new char[strlen(filename)+1];
  textsoftware_ = new char[255];
  filename_ = new char[strlen(filename)+1];

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, filename);
  strcpy(filename_, filename);

  setbitdepth( depth );

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

//Constructor for double levels, const char * filename
/////////////////////////////////////////////////////////////////////////
pngwriter::pngwriter(const unsigned & x, const unsigned & y,
		     const double & backgroundcolour,
		     const char * filename, const int & depth)
  : height_( y ), width_( x ),
    backgroundcolour_( int( std::max( 0., std::min( backgroundcolour, 1. ) * 65535) ) ),
    colortype_( 2 ), compressionlevel_( -2 ), transformation_( false ), filegamma_( 0.6 ),
    screengamma_( 2.2 ) 
{
  textauthor_ = new char[255];
  textdescription_ = new char[255];
  texttitle_ = new char[strlen(filename)+1];
  textsoftware_ = new char[255];
  filename_ = new char[strlen(filename)+1];

  strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
  strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
  strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
  strcpy(texttitle_, filename);
  strcpy(filename_, filename);

  setbitdepth( depth );

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;
}

int pngwriter::init_pixel_matrix()
{
  int nbrBytes( bit_depth_ / 8 * 3 );

  graph_ = new pngwriter::Byte*[ height_ ];//(png_bytepp)malloc(height_ * sizeof(png_bytep));
  if(graph_ == 0)
    {
      std::cerr << " PNGwriter::init_pixel_matrix - ERROR **:  Not able to allocate memory for image." << std::endl;
      return 1;
    }

  unsigned i;

  for ( i = 0; i < height_; i++)
    {
      graph_[i] = new pngwriter::Byte[ nbrBytes * width_ ];//(png_bytep)malloc(nbrBytes * width_ * sizeof(png_byte));
      if(graph_[i] == 0)
	{
	  std::cerr << " PNGwriter::init_pixel_matrix - ERROR **:  Not able to allocate memory for image." << std::endl;
	  return 2;
	}
    }

  if ( bit_depth_ == 16 )
    {
      for (  i = 0; i < height_ * nbrBytes * width_; i+=2 )
	{
	  graph_[ i / ( nbrBytes * width_ ) ][ i % ( nbrBytes * width_ ) ] =
	    Byte( backgroundcolour_ >> 8);
	  graph_[ i / ( nbrBytes * width_ ) ][ i % ( nbrBytes * width_ ) + 1 ] =
	    Byte( backgroundcolour_ % 256 );
	}
    }
  else
    {
       for (  i = 0; i < height_ * nbrBytes * width_; i++ )
	 graph_[ i / ( nbrBytes * width_ ) ][ i % ( nbrBytes * width_ ) ] =
	   Byte( backgroundcolour_ >> 8);
    }
  
  // unsigned tempindex, hhh, vhhh;
  // if ( bit_depth_ == 16 )
  //   {
  //     for( hhh = 0; hhh<width_;hhh++)
  // 	{
  // 	  for( vhhh = 0; vhhh<height_;vhhh++)
  // 	    {
  // 	      //etc
  // 	      tempindex = nbrBytes*hhh;
  // 	      graph_[vhhh][tempindex]   = Byte( backgroundcolour_ >> 8);
  // 	      graph_[vhhh][tempindex+1] = Byte(backgroundcolour_%256);
  // 	      graph_[vhhh][tempindex+2] = Byte (backgroundcolour_ >> 8);
  // 	      graph_[vhhh][tempindex+3] = Byte(backgroundcolour_%256);
  // 	      graph_[vhhh][tempindex+4] = Byte (backgroundcolour_ >> 8);
  // 	      graph_[vhhh][tempindex+5] = Byte(backgroundcolour_%256);
  // 	    }
  // 	}
  //   }
  // else
  //   {
  //     for( hhh = 0; hhh<width_;hhh++)
  // 	{
  // 	  for( vhhh = 0; vhhh<height_;vhhh++)
  // 	    {
  // 	      //etc
  // 	      tempindex = nbrBytes*hhh;
  // 	      graph_[vhhh][tempindex]   = Byte (backgroundcolour_ >> 8);
  // 	      graph_[vhhh][tempindex+1] = Byte (backgroundcolour_ >> 8);
  // 	      graph_[vhhh][tempindex+2] = Byte (backgroundcolour_ >> 8);
  // 	    }
  // 	}
  //   }

  return 0;
}

int pngwriter::init_pixel_matrix(Byte **source)
{
  int nbrBytes( bit_depth_ / 8 * 3 );

  //graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
  graph_ = new pngwriter::Byte*[ height_ ];
  if(graph_ == 0)
    {
      std::cerr << " PNGwriter::init_pixel_matrix - ERROR **:  Not able to allocate memory for image." << std::endl;
      return 1;
    }

  unsigned i;

  for ( i = 0; i < height_; i++)
    {
      //graph_[i] = (png_bytep)malloc(nbrBytes * width_ * sizeof(png_byte));
      graph_[i] = new pngwriter::Byte[ nbrBytes * width_ ];
      if(graph_[i] == 0)
	{
	  std::cerr << " PNGwriter::init_pixel_matrix - ERROR **:  Not able to allocate memory for image." << std::endl;
	  return 2;
	}
    }

  if(graph_ == 0)
    {
      std::cerr << " PNGwriter::init_pixel_matrix - ERROR **:  Not able to allocate memory for image." << std::endl;
      return 3;
    }

  for ( i = 0; i < height_ *  width_ * nbrBytes; i++ )
    graph_[ i / ( width_ * nbrBytes ) ][ i % ( width_ * nbrBytes ) ] =
      source[ i / ( width_ * nbrBytes ) ][ i % ( width_ * nbrBytes ) ];

  return 0;

  // if ( bit_depth_ == 16 )
  //   {
  //     for( hhh = 0; hhh < width_;hhh++ )
  // 	{
  // 	  for( vhhh = 0; vhhh < height_; vhhh++ )
  // 	    {
  // 	      //etc
  // 	      shift = nbrBytes * hhh;
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 1);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 2);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 3);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 4);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 5);
  // 	    }
  // 	}
  //   }
  // else
  //   {
  //     for( hhh = 0; hhh < width_;hhh++ )
  // 	{
  // 	  for( vhhh = 0; vhhh < height_; vhhh++ )
  // 	    {
  // 	      //etc
  // 	      shift = nbrBytes * hhh;
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 1);
  // 	      graph_[vhhh][shift] = (*source + vhhh * height_ + shift + 2);
  // 	    }
  // 	}
  //   }

  // return 0;
}

// Overloading operator =
/////////////////////////////////////////////////////////
pngwriter & pngwriter::operator = (const pngwriter & rhs)
{
  if( this == &rhs)
    {
      return *this;
    }

  std::cerr << "clearing memory..." << std::flush;
  // Delete current storage.
  for (unsigned jjj( 0 ); jjj < height_; jjj++) free( graph_[jjj] );
  free( graph_ );
 
  std::cerr << "done" << std::endl;

  width_ = rhs.width_;
  height_ = rhs.height_;
  backgroundcolour_ = rhs.backgroundcolour_;
  compressionlevel_ = rhs.compressionlevel_;
  filegamma_ = rhs.filegamma_;
  transformation_ = rhs.transformation_;

  std::cerr << "common copied" << std::endl;

  filename_ = new char[strlen(rhs.filename_)+1];
  textauthor_ = new char[strlen(rhs.textauthor_)+1];
  textdescription_ = new char[strlen(rhs.textdescription_)+1];
  textsoftware_ = new char[strlen(rhs.textsoftware_)+1];
  texttitle_ = new char[strlen(rhs.texttitle_)+1];

  std::cerr << "strings OK" << std::endl;

  strcpy(textauthor_, rhs.textauthor_);
  strcpy(textdescription_, rhs.textdescription_);
  strcpy(textsoftware_,rhs.textsoftware_);
  strcpy(texttitle_, rhs.texttitle_);
  strcpy(filename_, rhs.filename_);

  bit_depth_ = rhs.bit_depth_;
  colortype_= rhs.colortype_;
  screengamma_ = rhs.screengamma_;

  if ( init_pixel_matrix( rhs.graph_ ) > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	      << std::endl;

  return *this;
}

///////////////////////////////////////////////////////////////
void pngwriter::plot(const unsigned & x, const unsigned & y,
		     const pngwriter::Byte & red, const pngwriter::Byte & green,
		     const pngwriter::Byte & blue)
{
  if ( y >= height_ || x >= width_ )
    {
      //std::cerr << " PNGwriter::plot-- Plotting out of range! " << y << "   " << x << std::endl;
      return;
    }

  unsigned tempindex( x );

  if ( bit_depth_ == 16 )
    {
      tempindex *= 6;
      graph_[height_-y-1][tempindex]   = red;
      graph_[height_-y-1][tempindex+1] = Byte( 0 );
      graph_[height_-y-1][tempindex+2] = green;
      graph_[height_-y-1][tempindex+3] = Byte( 0 );
      graph_[height_-y-1][tempindex+4] = blue;
      graph_[height_-y-1][tempindex+5] = Byte( 0 );
    }
  else if ( bit_depth_ == 8 )
    {
      //	     graph_[height_-y][3*(x-1) + i] where i goes from 0 to 2
      tempindex *= 3;
      graph_[height_-y-1][tempindex]   = red;
      graph_[height_-y-1][tempindex+1] = green;
      graph_[height_-y-1][tempindex+2] = blue;
    }
}


void pngwriter::plot(const unsigned & x, const unsigned & y,
		     const int & red, const int & green, const int & blue)
{
  if( y >= height_ || x >= width_ )
    {
      //std::cerr << " PNGwriter::plot-- Plotting out of range! " << y << "   " << x << std::endl;
      return;
    }

  unsigned tempindex( x );

  // std::cout << x << ", " << y << std::endl;

  unsigned localR( std::max( 0, std::min( red, 65535 ) ) );
  unsigned localG( std::max( 0, std::min( green, 65535 ) ) );
  unsigned localB( std::max( 0, std::min( blue, 65535 ) ) );

  if ( bit_depth_ == 16 )
    {
      //graph_[height_-y][6*(x-1) + i] where i goes from 0 to 5
      tempindex *= 6;
      graph_[height_-y-1][tempindex]   = Byte(localR >> 8);
      graph_[height_-y-1][tempindex+1] = Byte(localR%256);
      graph_[height_-y-1][tempindex+2] = Byte(localG >> 8);
      graph_[height_-y-1][tempindex+3] = Byte(localG%256);
      graph_[height_-y-1][tempindex+4] = Byte(localB >> 8);
      graph_[height_-y-1][tempindex+5] = Byte(localB%256);
      
    }
  else if ( bit_depth_ == 8 )
    {
      //graph_[height_-y][3*(x-1) + i] where i goes from 0 to 2
      tempindex *= 3;
      graph_[height_-y-1][tempindex]   = Byte (localR >> 8);
      graph_[height_-y-1][tempindex+1] = Byte (localG >> 8);
      graph_[height_-y-1][tempindex+2] = Byte (localB >> 8);
    }
}

void pngwriter::plot(const unsigned & x, const unsigned & y,
		     const double & red, const double & green,
		     const double & blue)
{
  if ( bit_depth_ == 16 )
    plot( x, y, int( red * 65535. ), int( green * 65535. ),
	  int( blue * 65535. ) );
  else
    plot( x, y, Byte( red * 255. ), Byte( green * 255. ),
	  Byte( blue * 255. ) );
}

///////////////////////////////////////////////////////////////
int pngwriter::read(const unsigned & x, const unsigned & y,
		    const RGBColours & colour)
{
  if ( x >= width_ || y >= height_ )
    {
      return 0;
    }
      
  unsigned position( 0 );
  int returnedColour( 0 );

  switch ( colour )
    {
    case tRed:
    case tGreen:
    case tBlue:

      if ( bit_depth_ == 16 )
	{
	  position = 6 * x + unsigned( colour ) * 2;
	  returnedColour = graph_[ height_ - y - 1 ][ position ];
	  returnedColour <<= 8;
	  returnedColour += graph_[ height_ - y - 1 ][ ++position ];
	}
      else if ( bit_depth_ == 8 )
	{
	  position = 3 * x + unsigned( colour );
	  
	  returnedColour = graph_[ height_ - y - 1 ][ position ];
	  returnedColour <<= 8;
	}
      else
	{
	  std::cerr << " PNGwriter::read - WARNING **: Returning 0 because of bitdepth/colour type mismatch."<< std::endl;
	}
      break;
    default:
      std::cerr << " PNGwriter::read - WARNING **: Invalid argument: should be 1, 2 or 3, is " << unsigned( colour ) << std::endl;
    }

  return returnedColour;
}

///////////////////////////////////////////////////////////////
int pngwriter::read(const unsigned & x, const unsigned & y)
{
  int returnedColour( 0 );

  /*
   * An int is usually 4 bytes, then the division can be done at the
   * end, since the components are only 2 bytes.
   */
  returnedColour += read( x, y, tRed );
  returnedColour += read( x, y, tGreen );
  returnedColour += read( x, y, tBlue );

  return returnedColour / 3;
}

pngwriter::Byte pngwriter::cread(const unsigned & x, const unsigned & y,
		      const RGBColours & colour)
{
  if ( x >= width_ || y >= height_ )
    {
      return 0;
    }
      
  unsigned position( 0 );
  Byte returnedColour( 0 );

  switch ( colour )
    {
    case tRed:
    case tGreen:
    case tBlue:

      if ( bit_depth_ == 16 )
	{
	  std::cerr << " PNGwriter::read - WARNING **: You are reading a 16 bits image in a 8 bits variable, result will be truncated."<< std::endl;
	  position = 6 * x + ( unsigned( colour ) - 1 ) * 2;
	  returnedColour = Byte( graph_[ height_ - y - 1 ][ position ] << 8 );
	}
      else if ( bit_depth_ == 8 )
	{
	  position = 3 * x + ( unsigned( colour ) - 1 );
	  
	  returnedColour = graph_[ height_ - y - 1 ][ position ];
	}
      else
	{
	  std::cerr << " PNGwriter::read - WARNING **: Returning 0 because of bitdepth/colour type mismatch."<< std::endl;
	}
      break;
    default:
      std::cerr << " PNGwriter::read - WARNING **: Invalid argument: should be 1, 2 or 3, is " << unsigned( colour ) << std::endl;
    }

  return returnedColour;
}

pngwriter::Byte pngwriter::cread(const unsigned & x, const unsigned & y)
{
  Byte returnedColour( 0 );

  /*
   * Beware here : the return type can saturate the variable! It must
   * be divided for each component separetely.
   *
   * 0xA1 + 0xE5 = 0x186, truncated to 0x86 !
   */
  returnedColour += cread( x, y, tRed ) / 3;
  returnedColour += cread( x, y, tGreen ) / 3;
  returnedColour += cread( x, y, tBlue ) / 3;

  return returnedColour;
}

/////////////////////////////////////////////////////
double  pngwriter::dread(const unsigned & x, const unsigned & y,
			 const RGBColours & colour)
{
  /*
   * In order not to dupplicate the code, we use the integer version
   * here. It's the one that gives the highest precision.
   */
  double returnedColour( read( x, y, colour ) );
  returnedColour /= 65535.;

  return returnedColour;
}

double  pngwriter::dread(const unsigned & x, const unsigned & y)
{
  /*
   * In order not to dupplicate the code, we use the integer version
   * here. It's the one that gives the highest precision.
   */
  double returnedColour( read( x, y ) );
  returnedColour /= 65535.;

  return returnedColour;
}

///////////////////////////////////////////////////////
void pngwriter::clear()
{
  unsigned pen( 0 );
  unsigned pencil( 0 );
  unsigned tempindex( 0 );

  if(bit_depth_==16)
    {
      for(pen = 0; pen<width_;pen++)
	{
	  for(pencil = 0; pencil<height_;pencil++)
	    {
	      tempindex=6*pen;
	      graph_[pencil][tempindex] = 0;
	      graph_[pencil][tempindex+1] = 0;
	      graph_[pencil][tempindex+2] = 0;
	      graph_[pencil][tempindex+3] = 0;
	      graph_[pencil][tempindex+4] = 0;
	      graph_[pencil][tempindex+5] = 0;
	    }
	}
    }
  else if(bit_depth_==8)
    {
      for(pen = 0; pen<width_;pen++)
	{
	  for(pencil = 0; pencil<height_;pencil++)
	    {
	      tempindex=3*pen;
	      graph_[pencil][tempindex] = 0;
	      graph_[pencil][tempindex+1] = 0;
	      graph_[pencil][tempindex+2] = 0;
	    }
	}
    }
}

/////////////////////////////////////////////////////
void pngwriter::pngwriter_rename(char * newname)
{
  delete [] filename_;
  delete [] texttitle_;

  filename_ = new char[strlen(newname)+1];
  texttitle_ = new char[strlen(newname)+1];

  strcpy(filename_,newname);
  strcpy(texttitle_,newname);
}

///////////////////////////////////////////////////////
void pngwriter::pngwriter_rename(const char * newname)
{
  delete [] filename_;
  delete [] texttitle_;

  filename_ = new char[strlen(newname)+1];
  texttitle_ = new char[strlen(newname)+1];

  strcpy(filename_,newname);
  strcpy(texttitle_,newname);
}

///////////////////////////////////////////////////////
void pngwriter::pngwriter_rename(const long unsigned & index)
{
  char buffer[255];

  //   %[flags][width][.precision][modifiers]type
  //
  if((index > 999999999))
    {
      std::cerr << " PNGwriter::pngwriter_rename - ERROR **: Numerical name is out of 0 - 999 999 999 range (" << index <<")." << std::endl;
      return;
    }
  
  if( 0>  sprintf(buffer, "%9.9lu.png",index))
    {
      std::cerr << " PNGwriter::pngwriter_rename - ERROR **: Error creating numerical filename." << std::endl;
      return;
    }

  delete [] filename_;
  delete [] texttitle_;

  filename_ = new char[strlen(buffer)+1];
  texttitle_ = new char[strlen(buffer)+1];

  strcpy(filename_,buffer);
  strcpy(texttitle_,buffer);

}

///////////////////////////////////////////////////////
void pngwriter::settext(char * title, char * author, char * description,
			char * software)
{
  delete [] textauthor_;
  delete [] textdescription_;
  delete [] texttitle_;
  delete [] textsoftware_;

  textauthor_ = new char[strlen(author)+1];
  textdescription_ = new char[strlen(description)+1];
  textsoftware_ = new char[strlen(software)+1];
  texttitle_ = new char[strlen(title)+1];

  strcpy(texttitle_, title);
  strcpy(textauthor_, author);
  strcpy(textdescription_, description);
  strcpy(textsoftware_, software);
}

///////////////////////////////////////////////////////
void pngwriter::settext(const char * title, const char * author,
			const char * description, const char * software)
{
  delete [] textauthor_;
  delete [] textdescription_;
  delete [] texttitle_;
  delete [] textsoftware_;

  textauthor_ = new char[strlen(author)+1];
  textdescription_ = new char[strlen(description)+1];
  textsoftware_ = new char[strlen(software)+1];
  texttitle_ = new char[strlen(title)+1];

  strcpy(texttitle_, title);
  strcpy(textauthor_, author);
  strcpy(textdescription_, description);
  strcpy(textsoftware_, software);
}

///////////////////////////////////////////////////////
void pngwriter::close()
{
  FILE            *fp;
  png_structp     png_ptr;
  png_infop       info_ptr;

  fp = fopen(filename_, "wb");
  if( fp == 0)
    {
      std::cerr << " PNGwriter::close - ERROR **: Error creating file (fopen() returned 0 pointer)." << std::endl;
      perror(" PNGwriter::close - ERROR **");
      return;
    }

  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
  info_ptr = png_create_info_struct(png_ptr);
  png_init_io(png_ptr, fp);
  if(compressionlevel_ != -2)
    {
      png_set_compression_level(png_ptr, compressionlevel_);
    }
  else
    {
      png_set_compression_level(png_ptr, PNGWRITER_DEFAULT_COMPRESSION);
    }

  png_set_IHDR(png_ptr, info_ptr, width_, height_,
	       bit_depth_, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
	       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

  if(filegamma_ < 1.0e-1)
    {
      filegamma_ = 0.5;  // Modified in 0.5.4 so as to be the same as the usual gamma.
    }

  png_set_gAMA(png_ptr, info_ptr, filegamma_);

  time_t          gmt;
  png_time        mod_time;
  png_text        text_ptr[5];
  time(&gmt);
  png_convert_from_time_t(&mod_time, gmt);
  png_set_tIME(png_ptr, info_ptr, &mod_time);
  text_ptr[0].key = (char*)"Title";
  text_ptr[0].text = texttitle_;
  text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
  text_ptr[1].key = (char*)"Author";
  text_ptr[1].text = textauthor_;
  text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
  text_ptr[2].key = (char*)"Description";
  text_ptr[2].text = textdescription_;
  text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
  text_ptr[3].key = (char*)"Creation Time";
  text_ptr[3].text = (char*)png_convert_to_rfc1123(png_ptr, &mod_time);
  text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE;
  text_ptr[4].key = (char*)"Software";
  text_ptr[4].text = textsoftware_;
  text_ptr[4].compression = PNG_TEXT_COMPRESSION_NONE;
  png_set_text(png_ptr, info_ptr, text_ptr, 5);

  png_write_info(png_ptr, info_ptr);
  png_write_image(png_ptr, graph_);
  png_write_end(png_ptr, info_ptr);
  png_destroy_write_struct(&png_ptr, &info_ptr);
  fclose(fp);
}

//////////////////////////////////////////////////////
void pngwriter::master_line(const unsigned & xfrom, const unsigned & yfrom,
			    const unsigned & xto, const unsigned & yto,
			    const int & red, const int & green,
			    const int & blue, const double & opacity)
{
  //  Bresenham Algorithm.
  //
  if ( ( xfrom > height_ && xto > height_ ) ||
       ( yfrom > width_ && yto < width_ ) )
    {
      return;
    }
  
  int x1( xfrom ), x2( xto ), y1( yfrom ), y2( yto );
  int dx, dy;
  
  bool blend( std::abs( opacity - 1. ) > 1.e-7 );
  
  if ( x1 > x2 )
    {
      x1 = xto;
      y1 = yto;
      x2 = xfrom;
      y2 = yfrom;
    }

  int limit;

  if ( (dx = x2 - x1) != 0 )
    {
    if ( dx > 0 )
      {
	if ( ( dy = y2 - y1 ) != 0 )
	  {
	if ( dy > 0 )
	  {
	    // diagonal vector in 1st quadrant
	    if ( dx >= dy )
	      {
		// vector in 1st octant
		int e ( dx ); limit = std::min( x2, int( height_ ) );
		dx *= 2; dy *= 2;
		do {
		  blend ? plot_blend( x1, y1, opacity, red, green, blue ) :
		    plot( x1, y1, red, green, blue );
		  if ( (e -= dy) < 0 )
		    {
		      y1++;
		      e += dx;
		    }
		} while ( ++x1 != limit );
	      }
	    else
	      {
		// vector in 2nd octant
		int e( dy ); limit = std::min( y2, int( width_ ) );
		dx *= 2; dy *= 2;
		do {
		  blend ? plot_blend( x1, y1, opacity, red, green, blue ) :
		    plot( x1, y1, red, green, blue );
		  if ( (e -= dx) < 0 )
		    {
		      x1++;
		      e+= dy;
		    }   
		}  while ( ++y1 != limit );
	      }
	  }
	else
	  { // dy < 0 (and dx > 0)
	  // diagonal vector in 4th quadrant
	    if ( dx >= -dy )
	      {
		// vector in 8th octant
		int e( dx ); limit = std::min( x2, int( height_ ) );
		dx *= 2; dy *= 2;
		do {
		  blend ? plot_blend( x1, y1, opacity, red, green, blue ) :
		    plot( x1, y1, red, green, blue );
		  if ( (e += dy) < 0 )
		    {
		      y1--;
		      e += dx;
		    }
		} while ( ++x1 != limit );
	      }
	    else
	      {
		// vector in 7th octant
		int e( dy ); limit = std::max( y2, 0 );
		dx *= 2; dy *= 2;
		do {
		  blend ? plot_blend( x1, y1, opacity, red, green, blue ) :
		    plot( x1, y1, red, green, blue );
		  if ( (e += dx) > 0 )
		    {
		      x1++;
		      e += dy;
		    }
		} while ( --y1 != limit );
	      }
	  }
	  }
	else
	  { // dy == 0 (and dx > 0)
	    limit = std::min( x2, int( height_ ) );
	    // horizontal vector, towards right
	    do
	      {
		blend ? plot_blend( x1, y1, opacity, red, green, blue ) :
		  plot( x1, y1, red, green, blue );
	      } while ( ++x1 != limit );
	  }
      }
    else
      { // dx < 0
	if ( (dy = y2 - y1) != 0 )
	  {
	    if ( dy > 0 )
	      {
		// diagonal vector in 2nd quadrant
		if ( -dx >= dy )
		  {
		    // vector in 4th octant
		    int e( dx ); limit = std::max( x2, 0 );
		    dx *= 2; dy *= 2;
		    do
		      {
			blend ?
			  plot_blend( x1, y1, opacity, red, green, blue ) :
			  plot( x1, y1, red, green, blue );
			e += dy;
			if ( e >= 0 )
			  {
			    y1++;
			    e += dx;
			  }
		      } while ( --x1 != limit );
		  }
		else
		  {
		    // vector in 3rd octant
		    int e( dx ); limit = std::min( y2, int( width_ ) );
		    dx *= 2; dy *= 2;
		    do
		      {
			blend ?
			  plot_blend( x1, y1, opacity, red, green, blue ) :
			  plot( x1, y1, red, green, blue );
			e += dx;
			if ( e <= 0 )
			  {
			    x1--;
			    e += dy;
			  } 
		      } while ( ++y1 != limit );
		  }
       	      }
	    else
	      { // dy < 0 (et dx < 0)
		// diagonal vector in 3rd quadrant
		if ( dx <= dy )
		  {
		    // vector in 5th octant
		    int e( dx ); limit = std::max( x2, 0 );
		    dx *= 2; dy *= 2;
		    do
		      {
			blend ?
			  plot_blend( x1, y1, opacity, red, green, blue ) :
			  plot( x1, y1, red, green, blue );
			if ( (e -= dy) >= 0 )
			  {
			    y1--;
			    e += dx;
			  }
		      } while ( --x1 != limit );
		  }
		else
		  { // vector in 6th octant
		    int e( dx ); limit = std::max( y2, 0 );
		    dx *= 2; dy *= 2;
		    do
		      {
			blend ? plot_blend( x1, y1, opacity, red, green, blue ) :
			  plot( x1, y1, red, green, blue );
			if ( (e -= dx) >= 0 )
			  {
			    x1--;
			    e += dy;
			  }
		      } while ( --y1 != limit );
		  }
	      }
	  }
	else
	  {  // dy = 0 (et dx < 0)
	    limit = std::max( x2, 0 );
	    // horizontal vector towards left
	    do
	      {
		blend ?
		  plot_blend( x1, y1, opacity, red, green, blue ) :
		  plot( x1, y1, red, green, blue );
	      } while ( --x1 != limit );
	  }
      }
    }
  else
    { // dx = 0
      if ( (dy = y2 - y1) != 0 )
	{
	  if ( dy > 0 )
	    {
	      limit = std::min( y2, int( width_ ) );
	      // vertical vector going up
	      do
		{
		  blend ?
		    plot_blend( x1, y1, opacity, red, green, blue ) :
		    plot( x1, y1, red, green, blue );
		} while ( ++y1 != limit );
	    }
	  else
	    { // dy < 0 (et dx = 0)
	      limit = std::max( y2, 0 );
	      // vertical vector going down
	      do
		{
		  blend ?
		    plot_blend( x1, y1, opacity, red, green, blue ) :
		    plot( x1, y1, red, green, blue );
		} while ( --y1 != limit );
	    }
	}
    }

  /*
    int dy( yto );
    dy -= int( yfrom );
    int dx( xto );
    dy -= int( xfrom );
    int stepx, stepy;

    if (dy < 0)
    {
    dy = -dy;  stepy = -1;
    }
    else
    {
    stepy = 1;
    }

    if (dx < 0)
    {
    dx = -dx;  stepx = -1;
    }
    else
    {
    stepx = 1;
    }
    dy <<= 1;     // dy is now 2*dy
    dx <<= 1;     // dx is now 2*dx

    int x(xfrom), y(yfrom);

    plot( unsigned( x ), unsigned( y ), red, green, blue );

    if (dx > dy)
    {
    int fraction = dy - (dx >> 1);

    while ( unsigned( x ) != xto )
    {
    if (fraction >= 0)
    {
    y += stepy;
    fraction -= dx;
    }
    x += stepx;
    fraction += dy;
    plot( unsigned( x ), unsigned( y ), red, green, blue );
    }
    }
    else
    {
    int fraction = dx - (dy >> 1);
    while ( unsigned( y ) != yto )
    {
    if (fraction >= 0)
    {
    x += stepx;
    fraction -= dy;
    }
    y += stepy;
    fraction += dx;
    plot( unsigned( x ), unsigned( y ), red, green, blue );
    }
    } 
  */
}

void pngwriter::line(const unsigned & xfrom, const unsigned & yfrom,
		     const unsigned & xto, const unsigned & yto,
		     const int & red, const int & green, const int & blue)
{
  master_line( xfrom, yfrom, xto, yto, red, green, blue );
}

void pngwriter::line(const unsigned & xfrom, const unsigned & yfrom,
		     const unsigned & xto, const unsigned & yto,
		     const pngwriter::Byte & red, const pngwriter::Byte & green,const pngwriter::Byte &  blue)

{
  line( xfrom, yfrom, xto, yto, int( red ) << 8, int( green ) << 8,
	int( blue ) << 8 );
}

void pngwriter::line(const unsigned & xfrom, const unsigned & yfrom,
		     const unsigned & xto, const unsigned & yto,
		     const double & red, const double & green,
		     const double & blue)
{
  line( xfrom, yfrom, xto, yto,  int( red * 65535. ), int( green * 65535. ),
	int( blue * 65535. ) );
}

///////////////////////////////////////////////////////////////////////////////////////////
void pngwriter::master_square(const unsigned & xfrom, const unsigned & yfrom,
			      const unsigned & xto, const unsigned & yto,
			      const int & red, const int & green,
			      const int & blue, const double & opacity)
{
  master_line( xfrom, yfrom, xfrom, yto  , red, green, blue, opacity );
  master_line( xto  , yfrom, xto  , yto  , red, green, blue, opacity );
  master_line( xfrom, yfrom, xto  , yfrom, red, green, blue, opacity );
  master_line( xfrom, yto  , xto  , yto  , red, green, blue, opacity );
}

void pngwriter::square(const unsigned & xfrom, const unsigned & yfrom,
		       const unsigned & xto, const unsigned & yto,
		       const int & red, const int & green, const int & blue)
{
  master_square( xfrom, yfrom, xto, yto, red, green, blue );
}

void pngwriter::square(const unsigned & xfrom, const unsigned & yfrom,
		       const unsigned & xto, const unsigned & yto,
		       const pngwriter::Byte & red, const pngwriter::Byte & green,const pngwriter::Byte & blue)
{
  master_square( xfrom, yfrom, xto, yto, int( red ) << 8, int( green ) << 8,
		 int( blue ) << 8 );
}

void pngwriter::square(const unsigned & xfrom, const unsigned & yfrom,
		       const unsigned & xto, const unsigned & yto,
		       const double & red, const double & green,
		       const double & blue)
{
  master_square( xfrom, yfrom, xto, yto, int( red * 65535. ),
		 int( green * 65535. ), int( blue * 65535. ) );
}

//////////////////////////////////////////////////////////////////////////////////////////////////
void pngwriter::master_filledsquare(const unsigned & xfrom,
				    const unsigned & yfrom,
				    const unsigned & xto,
				    const unsigned & yto,
				    const int & red, const int & green,
				    const int & blue,
				    const double & opacity)
{
  for ( unsigned x( xfrom ); x <= xto; x++ )
    master_line( x, yfrom, x, yto, red, green, blue, opacity );
}

void pngwriter::filledsquare(const unsigned & xfrom,
			     const unsigned & yfrom,
			     const unsigned & xto,
			     const unsigned & yto,
			     const int & red, const int & green,
			     const int & blue)
{
  master_filledsquare( xfrom,  yfrom, xto, yto, red, green, blue );
}

void pngwriter::filledsquare(const unsigned & xfrom, const unsigned & yfrom,
			     const unsigned & xto, const unsigned & yto,
			     const pngwriter::Byte & red, const pngwriter::Byte & green,
			     const pngwriter::Byte & blue)
{
  master_filledsquare( xfrom, yfrom, xto, yto, int( red ) >> 8,
		       int( green ) >> 8 , int( blue ) >> 8  );
}

void pngwriter::filledsquare(const unsigned & xfrom, const unsigned & yfrom,
			     const unsigned & xto, const unsigned & yto,
			     const double & red, const double & green,
			     const double & blue)
{
  master_filledsquare( xfrom, yfrom, xto, yto, int( red * 65535. ),
		       int( green * 65535. ), int( blue * 65535. ) );
}

//////////////////////////////////////////////////////////////////////////////////////////////////
void pngwriter::master_circle(const unsigned & xcentre,
			      const unsigned & ycentre,
			      const unsigned & radius,
			      const int & red, const int & green,
			      const int & blue, const double & opacity)
{
  unsigned x( 0 );
  unsigned y( radius );
  int p( (5 - int( radius ) * 4 ) / 4 );

  master_circle_aux( xcentre, ycentre, x, y, red, green, blue, opacity );
  while (x < y)
    {
      x++;
      if (p < 0)
	{
	  p += 2*x+1;
	}
      else
	{
	  y--;
	  p += 2*(x-y)+1;
	}
      master_circle_aux( xcentre, ycentre, x, y, red, green, blue, opacity );
    }
}

void pngwriter::circle(const unsigned & xcentre,
		       const unsigned & ycentre,
		       const unsigned & radius,
		       const int & red, const int & green, const int & blue)
{
  master_circle( xcentre, ycentre, radius, red, green, blue );
}
void pngwriter::circle(const unsigned & xcentre,
		       const unsigned & ycentre,
		       const unsigned & radius,
		       const pngwriter::Byte & red, const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_circle( xcentre, ycentre, radius, int( red ) << 8, int( green ) << 8,
		 int( blue ) << 8 );
}

void pngwriter::circle(const unsigned & xcentre,
		       const unsigned & ycentre,
		       const unsigned & radius,
		       const double & red, const double & green,
		       const double & blue)
{
  master_circle( xcentre, ycentre, radius, double( red * 65535. ),
		 double( green * 65535. ), double( blue * 65535. ) );
}

////////////////////////////////////////////////////////////

void pngwriter::master_circle_aux(const unsigned & xcentre,
				  const unsigned & ycentre,
				  const unsigned & x,
				  const unsigned & y,
				  const int & red, const int & green,
				  const int & blue, const double & opacity)
{
  if ( std::abs( opacity - 1. ) < 1.e-7 )
    {
      if ( x == 0 )
	{
	  plot( xcentre, ycentre + y, red, green, blue);
	  plot( xcentre, ycentre - y, red, green, blue);
	  plot( xcentre + y, ycentre, red, green, blue);
	  plot( xcentre - y, ycentre, red, green, blue);
	}
      else
	if (x == y)
	  {
	    plot( xcentre + x, ycentre + y, red, green, blue);
	    plot( xcentre - x, ycentre + y, red, green, blue);
	    plot( xcentre + x, ycentre - y, red, green, blue);
	    plot( xcentre - x, ycentre - y, red, green, blue);
	  }
	else
	  if (x < y)
	    {
	      plot( xcentre + x, ycentre + y, red, green, blue);
	      plot( xcentre - x, ycentre + y, red, green, blue);
	      plot( xcentre + x, ycentre - y, red, green, blue);
	      plot( xcentre - x, ycentre - y, red, green, blue);
	      plot( xcentre + y, ycentre + x, red, green, blue);
	      plot( xcentre - y, ycentre + x, red, green, blue);
	      plot( xcentre + y, ycentre - x, red, green, blue);
	      plot( xcentre - y, ycentre - x, red, green, blue);
	    }
    }
  else // real opacity
    {
      if ( x == 0 )
	{
	  plot_blend( xcentre, ycentre + y, opacity, red, green, blue);
	  plot_blend( xcentre, ycentre - y, opacity, red, green, blue);
	  plot_blend( xcentre + y, ycentre, opacity, red, green, blue);
	  plot_blend( xcentre - y, ycentre, opacity, red, green, blue);
	}
      else
	if (x == y)
	  {
	    plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue);
	    plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue);
	    plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue);
	    plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue);
	  }
	else
	  if (x < y)
	    {
	      plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue);
	      plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue);
	      plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue);
	      plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue);
	      plot_blend( xcentre + y, ycentre + x, opacity, red, green, blue);
	      plot_blend( xcentre - y, ycentre + x, opacity, red, green, blue);
	      plot_blend( xcentre + y, ycentre - x, opacity, red, green, blue);
	      plot_blend( xcentre - y, ycentre - x, opacity, red, green, blue);
	    }
    }
}

void pngwriter::circle_aux(const unsigned & xcentre,
			   const unsigned & ycentre,
			   const unsigned & x,
			   const unsigned & y,
			   const int & red, const int & green, const int & blue)
{
  return master_circle_aux( xcentre, ycentre, x, y, red, green, blue );
}

////////////////////////////////////////////////////////////
void pngwriter::master_filledcircle(const unsigned & xcentre,
				    const unsigned & ycentre,
				    const unsigned & radius,
				    const int & red, const int & green,
				    const int & blue, const double & opacity)
{
  unsigned tempX1, tempX2;
  for(unsigned jjj( ycentre-radius ); jjj <= ycentre+radius; jjj++ )
    {
      tempX1 = xcentre - unsigned( sqrt( (double)(radius*radius) -
					 (-ycentre + jjj)*(-ycentre + jjj )));
      tempX2 = xcentre + unsigned( sqrt(( double)(radius*radius) -
					(-ycentre + jjj)*(-ycentre + jjj )));
      master_line( tempX1, jjj, tempX2, jjj, red, green, blue);
    }
}

void pngwriter::filledcircle(const unsigned & xcentre,
			     const unsigned & ycentre,
			     const unsigned & radius,
			     const int & red, const int & green,
			     const int & blue)
{
  master_filledcircle( xcentre, ycentre, radius, red, green, blue );
}

void pngwriter::filledcircle(const unsigned & xcentre,
			     const unsigned & ycentre,
			     const unsigned & radius,
			     const pngwriter::Byte & red, const pngwriter::Byte & green,
			     const pngwriter::Byte & blue)
{
  master_filledcircle( xcentre, ycentre,  radius, int( red ) << 8,
		       int( green ) << 8, int( blue ) << 8 );
}

void pngwriter::filledcircle(const unsigned & xcentre,
			     const unsigned & ycentre,
			     const unsigned & radius,
			     const double & red, const double & green,
			     const double & blue)
{
  master_filledcircle( xcentre, ycentre,  radius, int( red * 65535. ),
		       int( green * 65535. ), int( blue * 65535. ) );
}

////////////////Reading routines/////////////////////
/////////////////////////////////////////////////

// Modified with Mikkel's patch
void pngwriter::readfromfile(char * name)
{
  FILE            *fp;
  png_structp     png_ptr;
  png_infop       info_ptr;
  Byte   **image;
#if (PNG_LIBPNG_VER > 10500 )
  unsigned   width, height;
#else
  unsigned long width, height;
#endif
  int bit_depth, color_type, interlace_type;
  //   png_uint_32     i;
  //
  fp = fopen (name,"rb");
  if (fp==0)
    {
      std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file \"" << std::flush;
      std::cerr << name <<std::flush;
      std::cerr << "\"." << std::endl << std::flush;
      perror(" PNGwriter::readfromfile - ERROR **");
      return;
    }

  if(!check_if_png(name, &fp))
    {
      std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". This may not be a valid png file. (check_if_png() failed)." << std::endl;
      // fp has been closed already if check_if_png() fails.
      return;
    }

  //   Code as it was before Sven's patch
  /*   if(!read_png_info(fp, &png_ptr, &info_ptr))
       {
       std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". read_png_info() failed." << std::endl;
       // fp has been closed already if read_png_info() fails.
       return;
       }

       if(!read_png_image(fp, png_ptr, info_ptr, &image, &width, &height))
       {
       std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". read_png_image() failed." << std::endl;
       // fp has been closed already if read_png_image() fails.
       return;
       }

       //stuff should now be in image[][].

       */ //End of code as it was before Sven's patch.
   
  //Sven's patch starts here
  ////////////////////////////////////
 
  /*
     
    if(!read_png_info(fp, &png_ptr, &info_ptr)) 
    {
	 
    std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". read_png_info() failed." << std::endl; 
    // fp has been closed already if read_png_info() fails. 
    return; 
    } 
   
    // UPDATE: Query info struct to get header info BEFORE reading the image  
   
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, 0, 0); 
    bit_depth_ = bit_depth; 
    colortype_ = color_type; 
   
    if(color_type == PNG_COLOR_TYPE_PALETTE) 
    { 
    png_set_expand(png_ptr); 
    png_read_update_info(png_ptr, info_ptr); 
    } 
   
    if(!read_png_image(fp, png_ptr, info_ptr, &image, &width, &height)) 
    { 
    std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". read_png_image() failed." << std::endl; 
    // fp has been closed already if read_png_image() fails. 
    return; 
    } 
   
    //stuff should now be in image[][]. 
    */
  //Sven's patch ends here.
  ////////////////////////////////
   
  // Mikkel's patch starts here
  // ///////////////////////////////////
   
  if(!read_png_info(fp, &png_ptr, &info_ptr)) 
    {
	 
      std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". read_png_info() failed." << std::endl; 
      // fp has been closed already if read_png_info() fails. 
      return; 
    } 
   
  //Input transformations  
  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, 0, 0); 
  bit_depth_ = bit_depth; 
  colortype_ = color_type; 
   
    
  // Changes palletted image to RGB
  if(color_type == PNG_COLOR_TYPE_PALETTE /*&& bit_depth<8*/) 
    { 
      // png_set_expand(png_ptr);  
      png_set_palette_to_rgb(png_ptr);  // Just an alias of png_set_expand()
      transformation_ = true; 
    } 
   
  // Transforms grescale images of less than 8 bits to 8 bits.
  if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth<8) 
    { 
      // png_set_expand(png_ptr);
#if (PNG_LIBPNG_VER > 10290)
      png_set_expand_gray_1_2_4_to_8(png_ptr);  // Just an alias of the above.
#else
      png_set_gray_1_2_4_to_8(png_ptr);  // Just an alias of the above.
#endif
      transformation_ = true; 
    } 
   
  // Completely strips the alpha channel.
  if(color_type & PNG_COLOR_MASK_ALPHA) 
    { 
      png_set_strip_alpha(png_ptr); 
      transformation_ = true; 
    } 
   
  // Converts greyscale images to RGB.
  if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) // Used to be RGB, fixed it. 
    { 
      png_set_gray_to_rgb(png_ptr); 
      transformation_ = true; 
    } 
	 
  // If any of the above were applied,
  if(transformation_) 
    { 
      // png_set_gray_to_rgb(png_ptr);   //Is this really needed here?
	 
      // After setting the transformations, libpng can update your png_info structure to reflect any transformations 
      // you've requested with this call. This is most useful to update the info structure's rowbytes field so you can 
      // use it to allocate your image memory. This function will also update your palette with the correct screen_gamma 
      // and background if these have been given with the calls above.
	 
      png_read_update_info(png_ptr, info_ptr); 
	
      // Just in case any of these have changed?
      png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, 0, 0); 
      bit_depth_ = bit_depth; 
      colortype_ = color_type; 
    } 
   
  if(!read_png_image(fp, png_ptr, info_ptr, &image, &width, &height)) 
    { 
      std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". read_png_image() failed." << std::endl; 
      // fp has been closed already if read_png_image() fails. 
      return; 
    } 
   
  //stuff should now be in image[][].
   
  // Mikkel's patch ends here
  // //////////////////////////////
  // 
  if( image == 0)
    {
      std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file " << name << ". Can't assign memory (after read_png_image(), image is 0)." << std::endl;
      fclose(fp);
      return;
    }

  //First we must get rid of the image already there, and free the memory.
  unsigned jjj;
  for (jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
  free(graph_);

  //Must reassign the new size of the read image
  width_ = width;
  height_ = height;

  //Graph now is the image.
  graph_ = image;

  rowbytes_ = png_get_rowbytes(png_ptr, info_ptr);

  // This was part of the original source, but has been moved up.
  /*
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, 0, 0);
    bit_depth_ = bit_depth;
    colortype_ = color_type;
  */
  // if(color_type == PNG_COLOR_TYPE_PALETTE /*&& bit_depth<8*/   )
  /*    {
	png_set_expand(png_ptr);
	}

	if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth<8)
	{
	png_set_expand(png_ptr);
	}

	if(color_type & PNG_COLOR_MASK_ALPHA)
	{
	png_set_strip_alpha(png_ptr);
	}

	if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
	{
	png_set_gray_to_rgb(png_ptr);
	}

  */

  if((bit_depth_ !=16)&&(bit_depth_ !=8))
    {
      std::cerr << " PNGwriter::readfromfile() - WARNING **: Input file is of unsupported type (bad bit_depth). Output will be unpredictable.\n";
    }

  // Thanks to Mikkel's patch, PNGwriter should now be able to handle these color types:

  /* 
     color_type     - describes which color/alpha channels are present.
     PNG_COLOR_TYPE_GRAY                        (bit depths 1, 2, 4, 8, 16)
     PNG_COLOR_TYPE_GRAY_ALPHA                        (bit depths 8, 16)
     PNG_COLOR_TYPE_PALETTE                        (bit depths 1, 2, 4, 8)
     PNG_COLOR_TYPE_RGB                        (bit_depths 8, 16)
     PNG_COLOR_TYPE_RGB_ALPHA                        (bit_depths 8, 16)

     PNG_COLOR_MASK_PALETTE
     PNG_COLOR_MASK_COLOR
					 
     color types.  Note that not all combinations are legal 
     #define PNG_COLOR_TYPE_GRAY 0
     #define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR (2) | PNG_COLOR_MASK_PALETTE (1) )
     #define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR (2) )
     #define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR (2) | PNG_COLOR_MASK_ALPHA (4) )
     #define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA (4) )

     aliases 
     #define PNG_COLOR_TYPE_RGBA  PNG_COLOR_TYPE_RGB_ALPHA
     #define PNG_COLOR_TYPE_GA  PNG_COLOR_TYPE_GRAY_ALPHA

     These describe the color_type field in png_info. 
     color type masks 
     #define PNG_COLOR_MASK_PALETTE    1
     #define PNG_COLOR_MASK_COLOR      2
     #define PNG_COLOR_MASK_ALPHA      4


  */
					 
					 
  if(colortype_ !=2)
    {
      std::cerr << " PNGwriter::readfromfile() - WARNING **: Input file is of unsupported type (bad color_type). Output will be unpredictable.\n";
    }

  screengamma_ = 2.2;
  double          file_gamma,screen_gamma;
  screen_gamma = screengamma_;
  if (png_get_gAMA(png_ptr, info_ptr, &file_gamma))
    {
      png_set_gamma(png_ptr,screen_gamma,file_gamma);
    }
  else
    {
      png_set_gamma(png_ptr, screen_gamma,0.45);
    }

  filegamma_ = file_gamma;

  fclose(fp);
}

///////////////////////////////////////////////////////

void pngwriter::readfromfile(const char * name)
{
  readfromfile(const_cast<char *>(name));
}

/////////////////////////////////////////////////////////
int pngwriter::check_if_png(char *file_name, FILE **fp)
{
  char    sig[PNG_BYTES_TO_CHECK];

  if ( /*(*fp = fopen(file_name, "rb")) */  *fp == 0) // Fixed 10 10 04
    {
      //   exit(EXIT_FAILURE);
      std::cerr << " PNGwriter::check_if_png - ERROR **: Could not open file  " << file_name << " to read." << std::endl;
      perror(" PNGwriter::check_if_png - ERROR **");
      return 0;
    }

  if (fread(sig, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK)
    {
      //exit(EXIT_FAILURE);
      std::cerr << " PNGwriter::check_if_png - ERROR **: File " << file_name << " does not appear to be a valid PNG file." << std::endl;
      perror(" PNGwriter::check_if_png - ERROR **");
      fclose(*fp);
      return 0;
    }
   
  if (png_sig_cmp( (png_bytep) sig, (png_size_t)0, PNG_BYTES_TO_CHECK) /*png_check_sig((png_bytep) sig, PNG_BYTES_TO_CHECK)*/ ) 
    {
      std::cerr << " PNGwriter::check_if_png - ERROR **: File " << file_name << " does not appear to be a valid PNG file. png_check_sig() failed." << std::endl;
      fclose(*fp);
      return 0;
    }
   
   
   
  return 1; //Success
}

///////////////////////////////////////////////////////
int pngwriter::read_png_info(FILE *fp, png_structp *png_ptr,
			     png_infop *info_ptr)
{
  *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
  if (*png_ptr == 0)
    {
      std::cerr << " PNGwriter::read_png_info - ERROR **: Could not create read_struct." << std::endl;
      fclose(fp);
      return 0;
      //exit(EXIT_FAILURE);
    }
  *info_ptr = png_create_info_struct(*png_ptr);
  if (*info_ptr == 0)
    {
      png_destroy_read_struct(png_ptr, (png_infopp)0, (png_infopp)0);
      std::cerr << " PNGwriter::read_png_info - ERROR **: Could not create info_struct." << std::endl;
      //exit(EXIT_FAILURE);
      fclose(fp);
      return 0;
    }
#if (PNG_LIBPNG_VER < 10500 )
  if (setjmp((*png_ptr)->jmpbuf)) /*(setjmp(png_jmpbuf(*png_ptr)) )*//////////////////////////////////////
#else
    if ( setjmp(png_jmpbuf (*png_ptr) ) )
#endif
    {
      png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)0);
      std::cerr << " PNGwriter::read_png_info - ERROR **: This file may be a corrupted PNG file. (setjmp(*png_ptr)->jmpbf) failed)." << std::endl;
      fclose(fp);
      return 0;
      //exit(EXIT_FAILURE);
    }
  png_init_io(*png_ptr, fp);
  png_set_sig_bytes(*png_ptr, PNG_BYTES_TO_CHECK);
  png_read_info(*png_ptr, *info_ptr);

  return 1;
}

////////////////////////////////////////////////////////////
int pngwriter::read_png_image(FILE *fp, png_structp png_ptr,
			      png_infop info_ptr, png_bytepp *image,
			      png_uint_32 *width, png_uint_32 *height)
{
  unsigned i,j;

  *width = png_get_image_width(png_ptr, info_ptr);
  *height = png_get_image_height(png_ptr, info_ptr);

  if( width == 0)
    {
      std::cerr << " PNGwriter::read_png_image - ERROR **: png_get_image_width() returned 0 pointer." << std::endl;
      fclose(fp);
      return 0;
    }

  if( height == 0)
    {
      std::cerr << " PNGwriter::read_png_image - ERROR **: png_get_image_height() returned 0 pointer." << std::endl;
      fclose(fp);
      return 0;
    }

  if ((*image = (png_bytepp)malloc(*height * sizeof(png_bytep))) == 0)
    {
      std::cerr << " PNGwriter::read_png_image - ERROR **: Could not allocate memory for reading image." << std::endl;
      fclose(fp);
      return 0;
      //exit(EXIT_FAILURE);
    }
  for (i = 0; i < *height; i++)
    {
      (*image)[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
      if ((*image)[i] == 0)
	{
	  for (j = 0; j < i; j++) free((*image)[j]);
	  free(*image);
	  fclose(fp);
	  std::cerr << " PNGwriter::read_png_image - ERROR **: Could not allocate memory for reading image." << std::endl;
	  return 0;
	  //exit(EXIT_FAILURE);
	}
    }
  png_read_image(png_ptr, *image);

  return 1;
}

///////////////////////////////////
const unsigned & pngwriter::getheight(void)
{
  return height_;
}

const unsigned & pngwriter::getwidth(void)
{
  return width_;
}

void pngwriter::setbitdepth(const int & depth)
{
  switch( depth )
    {
    case 1:
    case 8:
      bit_depth_ = 8;
      break;
    case 2:
    case 16:
      bit_depth_ = 16;
      break;
    default:
      std::cerr << " PNGwriter::setbitdepth - ERROR **: Called with wrong colour argument: should be 1, 2, 8 or 16; was: " << depth << "." << std::endl;
      bit_depth_ = 16;    
    }
}

const int & pngwriter::getbitdepth(void)
{
  return bit_depth_;
}

const int & pngwriter::getcolortype(void)
{
  return colortype_;
}

const double & pngwriter::getgamma(void)
{
  return filegamma_;
}

void pngwriter::setgamma(const double & gamma)
{
  filegamma_ = gamma;
}

// The algorithms HSVtoRGB and RGBtoHSV were found at http://www.cs.rit.edu/~ncs/
//  which is a page that belongs to Nan C. Schaller, though
//  these algorithms appear to be the work of Eugene Vishnevsky.
//////////////////////////////////////////////
void pngwriter::HSVtoRGB( double &r, double &g, double &b,
			  const double & h, const double & s, const double & v )
{
  // r,g,b values are from 0 to 1
  // h = [0,1], s = [0,1], v = [0,1]
  // if s == 0, then h = -1 (undefined)
  //

  int i;
  double f, p, q, t;
  if( s == 0 )
    {
      // achromatic (grey)
      r = g = b = v;
      return;
    }

  double hue( h * 6. );           // sector 0 to 5
  i = int(floor( hue ));
  f = hue - i;                      // factorial part of h
  p = v * ( 1 - s );
  q = v * ( 1 - s * f );
  t = v * ( 1 - s * ( 1 - f ) );

  if ( 0 <= h && h < 1./6. )
    {
      r = v;
      g = t;
      b = p;
    }
  else if ( 1./6. <= h && h < 1./3. )
    {
      r = q;
      g = v;
      b = p;
    }
  else if ( 1./3. <= h && h < 1./2. )
    {
      r = p;
      g = v;
      b = t;
    }
  else if ( 1./2. <= h && h < 2./3. )
    {
      r = p;
      g = q;
      b = v;
    }
  else if ( 2./3. <= h && h < 5./6. )
    {
      r = t;
      g = p;
      b = v;
    }
  else
    {
      r = v;
      g = p;
      b = q;
    }
}

void pngwriter::RGBtoHSV( const double & r, const double & g, const double & b,
			  double &h, double &s, double &v )
{

  double min=0.0; //These values are not used.
  double max=1.0;
  double delta;

  max = std::max( std::max( r, g ), b );
  min = std::min( std::min( r, g ), b );

  v = max;                               // v

  delta = max - min;

  if( max != 0 )
    s = delta / max;               // s
  else
    {
      //r = g = b = 0;                // s = 0, v is undefined
      s = 0;
      h = -1;
      return;
    }

  if( r == max )
    h = ( g - b ) / delta;         // between yellow & magenta
  else if( g == max )
    h = 2 + ( b - r ) / delta;     // between cyan & yellow
  else
    h = 4 + ( r - g ) / delta;     // between magenta & cyan

  h *= 60;                               // degrees
  if( h < 0 )
    h += 360;
}

//
//////////////////////////////////////////////////////////////////////////////////
void pngwriter::plotHSV(const unsigned & x, const unsigned & y,
			const pngwriter::Byte & hue, const pngwriter::Byte & saturation,
			const pngwriter::Byte & value)
{
  plotHSV( x, y, double( hue ) / 255., double( saturation ) / 255.,
	   double( value ) / 255.);
}

void pngwriter::plotHSV(const unsigned & x, const unsigned & y,
			const double & hue, const double & saturation,
			const double & value)
{
  /*
   * Since HSV requires RGB to HSV conversion, it's natural to define
   * the function for the double values.
   */
  double red,green,blue;

  HSVtoRGB(red,green,blue,hue,saturation,value);
  plot(x,y,red,green,blue);
}

void pngwriter::plotHSV(const unsigned & x, const unsigned & y,
			const int & hue, const int & saturation,
			const int & value)
{
  plotHSV(x, y, double( hue ) / 65535., double( saturation ) / 65535.,
	  double( value ) / 65535.);
}

//
//////////////////////////////////////////////////////////////////////////////////
double pngwriter::dreadHSV(const unsigned & x, const unsigned & y,
			   const HSVColours & colour)
{
  if( x < width_ && y < height_ )
    {
      double red,green,blue;
      double hue, saturation, value;

      red   = double( dread( x, y, tRed) );
      green = double( dread( x, y, tGreen) );
      blue  = double( dread( x, y, tBlue) );

      RGBtoHSV( red,  green,  blue, hue,  saturation, value );

      switch( colour )
	{
	case tHue:
	  return hue / 360.;
	  break;
	case tSaturation:
	  return saturation;
	  break;
	case tValue:
	  return value;
	  break;
	default:
	  std::cerr << " PNGwriter::dreadHSV - ERROR **: Called with wrong colour argument: should be 1, 2 or 3; was: " << colour << "." << std::endl;
	  return 0.;
	}
    }
  else
    {
      std::cerr << " PNGwriter::dreadHSV - ERROR **: Trying to access unexisting index." << std::endl;
      return 0.;
    }
}

//
//////////////////////////////////////////////////////////////////////////////////
int pngwriter::readHSV(const unsigned & x, const unsigned & y,
		       const HSVColours & colour)
{
  return int( dreadHSV( x, y, colour ) * 65535. );
}

//
//////////////////////////////////////////////////////////////////////////////////
pngwriter::Byte pngwriter::creadHSV(const unsigned & x, const unsigned & y,
			 const HSVColours & colour)
{
  return pngwriter::Byte( dreadHSV( x, y, colour ) * 255. );
}

void pngwriter::setcompressionlevel(const int & level)
{
  if( (level < -1)||(level > 9) )
    {
      std::cerr << " PNGwriter::setcompressionlevel - ERROR **: Called with wrong compression level: should be -1 to 9, was: " << level << "." << std::endl;
    }
  compressionlevel_ = level;
}

// An implementation of a Bezier curve.
void pngwriter::bezier( const int & startPtX, const int & startPtY,
			const int & startControlX, const int & startControlY,
			const int & endPtX, const int & endPtY,
			const int & endControlX, const int & endControlY,
			const double & red, const double & green,
			const double & blue)
{
  master_bezier( startPtX,  startPtY, startControlX, startControlY,
		 endPtX, endPtY, endControlX,  endControlY,
		 int( red * 65535. ), int( green * 65535. ),
		 int( blue * 65535. ) );
}

void pngwriter::bezier( const int & startPtX, const int & startPtY,
			const int & startControlX, const int & startControlY,
			const int & endPtX, const int & endPtY,
			const int & endControlX, const int & endControlY,
			const pngwriter::Byte & red, const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_bezier( startPtX,  startPtY, startControlX, startControlY,
		 endPtX, endPtY, endControlX,  endControlY,
		 int( red ) << 8, int( green ) << 8,
		 int( blue ) << 8 );
}

//int version of bezier
void pngwriter::bezier( const int & startPtX, const int & startPtY,
			const int & startControlX, const int & startControlY,
			const int & endPtX, const int & endPtY,
			const int & endControlX, const int & endControlY,
			const int & red, const int & green, const int & blue)
{
  master_bezier( startPtX, startPtY, startControlX, startControlY,
		 endPtX, endPtY, endControlX, endControlY,
		 red, green, blue );
}

void pngwriter::master_bezier( const int & startPtX, const int & startPtY,
			       const int & startControlX,
			       const int & startControlY,
			       const int & endPtX, const int & endPtY,
			       const int & endControlX, const int & endControlY,
			       const int & red, const int & green,
			       const int & blue, const double & opacity)
{
  double cx( 3. * double( startControlX - startPtX ) );
  double bx( 3. * double( endControlX - startControlX ) - cx );
  double ax( double( endPtX - startPtX ) - cx - bx );

  double cy( 3. * double( startControlY - startPtY ) );
  double by( 3. * double( endControlY - startControlY ) - cy );
  double ay( double( endPtY - startPtY - cy - by ) );

  double x( startPtX ), y( startPtY ), newx, newy, t;

  for( t = 0.0; t<=1.005; t += 0.005 )
    {
      newx = startPtX + t * cx + t * bx + t * ax;
      newy = startPtY + t * cy + t * by + t * ay;
      master_line( int( x ), int( y ), int( newx ), int( newy ),
		   red, green, blue, opacity );
      x = newx;
      y = newy;
    }
}

/*
  int pngwriter::getcompressionlevel(void)
  {
  return png_get_compression_level(png_ptr);
  }
*/

double pngwriter::version(void)
{
  const char *a = "Jeramy Webb (jeramyw@gmail.com), Mike Heller (mkheller@gmail.com)"; // For their generosity ;-)
  char b = a[27];
  b++;
  return (PNGWRITER_VERSION);
}

void pngwriter::write_png(void)
{
  close();
}

#ifndef NO_FREETYPE

// Freetype-based text rendering functions.
///////////////////////////////////////////
void pngwriter::master_plot_text( char * face_path, const int & fontsize,
				  const unsigned & x_start,
				  const unsigned & y_start,
				  const double & angle, char * text,
				  const int & red, const int & green,
				  const int & blue, const double & opacity)
{
  FT_Library  library;
  FT_Face     face;
  FT_Matrix   matrix;      // transformation matrix
  FT_Vector   pen;

  FT_UInt glyph_index;
  FT_Error error;

  FT_Bool use_kerning;
  FT_UInt previous = 0;

  /* Set up transformation Matrix */
  matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
  matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
  matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
  matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;

  /* Place starting coordinates in adequate form. */
  pen.x = x_start*64 ;
  pen.y =   (int)(y_start/64.0);

  /*Count the length of the string */
  int num_chars = strlen(text);

  /* Initialize FT Library object */
  error = FT_Init_FreeType( &library );
  if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not init Library."<< std::endl; return;}

  /* Initialize FT face object */
  error = FT_New_Face( library,face_path,0,&face );
  if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; }

  /* Set the Char size */
  error = FT_Set_Char_Size( face,          /* handle to face object           */
			    0,             /* char_width in 1/64th of points  */
			    fontsize*64,   /* char_height in 1/64th of points */
			    100,           /* horizontal device resolution    */
			    100 );         /* vertical device resolution      */

  /* A way of accesing the glyph directly */
  FT_GlyphSlot  slot = face->glyph;  // a small shortcut

  /* Does the font file support kerning? */
  use_kerning = FT_HAS_KERNING( face );

  int n;
  for ( n = 0; n < num_chars; n++ )
    {
      /* Convert character code to glyph index */
      glyph_index = FT_Get_Char_Index( face, text[n] );

      /* Retrieve kerning distance and move pen position */
      if ( use_kerning && previous&& glyph_index )
	{
	  FT_Vector  delta;
	  FT_Get_Kerning( face,
			  previous,
			  glyph_index,
			  ft_kerning_default, //FT_KERNING_DEFAULT,
			  &delta );

	  /* Transform this kerning distance into rotated space */
	  pen.x += (int) (((double) delta.x)*cos(angle));
	  pen.y +=  (int) (((double) delta.x)*( sin(angle)));
	}

      /* Set transform */
      FT_Set_Transform( face, &matrix, &pen );

      /*set char size*/

      if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Set char size error." << std::endl; return;}

      /* Retrieve glyph index from character code */
      glyph_index = FT_Get_Char_Index( face, text[n] );

      /* Load glyph image into the slot (erase previous one) */
      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
      if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}

      /* Convert to an anti-aliased bitmap */
      //	error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
      error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
      if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Render glyph error." << std::endl; return;}

      /* Now, draw to our target surface */
      master_my_draw_bitmap( &slot->bitmap,
			     slot->bitmap_left,
			     y_start + slot->bitmap_top,
			     red,
			     green,
			     blue, opacity );

      /* Advance to the next position */
      pen.x += slot->advance.x;
      pen.y += slot->advance.y;

      /* record current glyph index */
      previous = glyph_index;
    }

  /* Free the face and the library objects */
  FT_Done_Face    ( face );
  FT_Done_FreeType( library );
}

void pngwriter::plot_text( char * face_path, const int & fontsize,
			   const unsigned & x_start, const unsigned & y_start,
			   const double & angle, char * text,
			   const int & red, const int & green, const int & blue)
{
  master_plot_text( face_path, fontsize, x_start, y_start, angle, text,
		    red, green, blue );
}

void pngwriter::master_plot_text_utf8( char * face_path, const int & fontsize,
				       const unsigned & x_start,
				       const unsigned & y_start,
				       const double & angle,  char * text,
				       const int & red, const int & green,
				       const int & blue, const double & opacity)
{
  FT_Library  library;
  FT_Face     face;
  FT_Matrix   matrix;      // transformation matrix
  FT_Vector   pen;

  FT_UInt glyph_index;
  FT_Error error;

  FT_Bool use_kerning;
  FT_UInt previous = 0;

  /* Set up transformation Matrix */
  matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
  matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
  matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
  matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;

  /* Place starting coordinates in adequate form. */
  pen.x = x_start*64 ;
  pen.y = (int)(y_start/64.0);

  /*Count the length of the string */
  int num_bytes=0;
  while(text[num_bytes]!=0)
    {
      num_bytes++;
    }

  /*
    std::cout << "Num bytes is: "<< num_bytes << std::endl;
  */

  //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file.
  long * ucs4text;
  ucs4text = new long[num_bytes+1];

  Byte u,v,w,x,y,z;

  int num_chars=0;

  long iii=0;

  while(iii<num_bytes)
    {
      z = text[iii];

      if(z<=127)
	{
	  ucs4text[num_chars] = z;
	}

      if((192<=z)&&(z<=223))
	{
	  iii++; y = text[iii];
	  ucs4text[num_chars] = (z-192)*64 + (y -128);
	}

      if((224<=z)&&(z<=239))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  ucs4text[num_chars] = (z-224)*4096 + (y -128)*64 + (x-128);
	}

      if((240<=z)&&(z<=247))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  iii++; w = text[iii];
	  ucs4text[num_chars] = (z-240)*262144 + (y -128)*4096 + (x-128)*64 + (w-128);
	}

      if((248<=z)&&(z<=251))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  iii++; w = text[iii];
	  iii++; v = text[iii];
	  ucs4text[num_chars] = (z-248)*16777216 + (y -128)*262144 + (x-128)*4096 + (w-128)*64 +(v-128);
	}

      if((252==z)||(z==253))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  iii++; w = text[iii];
	  iii++; v = text[iii];
	  u = text[iii];
	  ucs4text[num_chars] = (z-252)*1073741824 + (y -128)*16777216   + (x-128)*262144 + (w-128)*4096 +(v-128)*64 + (u-128);
	}

      if((z==254)||(z==255))
	{
	  std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: Problem with character: invalid UTF-8 data."<< std::endl;
	}
      // std::cerr << "\nProblem at " << iii << ".\n";
      //
      iii++;
      num_chars++;
    }

  // num_chars now contains the number of characters in the string.
  /*
    std::cout << "Num chars is: "<< num_chars << std::endl;
  */

  /* Initialize FT Library object */
  error = FT_Init_FreeType( &library );
  if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Could not init Library."<< std::endl; return;}

  /* Initialize FT face object */
  error = FT_New_Face( library,face_path,0,&face );
  if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; }

  /* Set the Char size */
  error = FT_Set_Char_Size( face,          /* handle to face object           */
			    0,             /* char_width in 1/64th of points  */
			    fontsize*64,   /* char_height in 1/64th of points */
			    100,           /* horizontal device resolution    */
			    100 );         /* vertical device resolution      */

  /* A way of accesing the glyph directly */
  FT_GlyphSlot  slot = face->glyph;  // a small shortcut

  /* Does the font file support kerning? */
  use_kerning = FT_HAS_KERNING( face );

  int n;
  for ( n = 0; n < num_chars; n++ )
    {
      /* Convert character code to glyph index */
      glyph_index = FT_Get_Char_Index( face, ucs4text[n] );

      /* Retrieve kerning distance and move pen position */
      if ( use_kerning && previous&& glyph_index )
	{
	  FT_Vector  delta;
	  FT_Get_Kerning( face,
			  previous,
			  glyph_index,
			  ft_kerning_default, //FT_KERNING_DEFAULT,
			  &delta );

	  /* Transform this kerning distance into rotated space */
	  pen.x += (int) (((double) delta.x)*cos(angle));
	  pen.y +=  (int) (((double) delta.x)*( sin(angle)));
	}

      /* Set transform */
      FT_Set_Transform( face, &matrix, &pen );

      /*set char size*/

      if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Set char size error." << std::endl; return;}

      /* Retrieve glyph index from character code */
      glyph_index = FT_Get_Char_Index( face, ucs4text[n] );

      /* Load glyph image into the slot (erase previous one) */
      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
      if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}

      /* Convert to an anti-aliased bitmap */
      error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
      if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Render glyph error." << std::endl; return;}

      /* Now, draw to our target surface */
      master_my_draw_bitmap( &slot->bitmap,
			     slot->bitmap_left,
			     y_start + slot->bitmap_top,
			     red,
			     green,
			     blue, opacity );

      /* Advance to the next position */
      pen.x += slot->advance.x;
      pen.y += slot->advance.y;

      /* record current glyph index */
      previous = glyph_index;
    }

  /* Free the face and the library objects */
  FT_Done_Face    ( face );
  FT_Done_FreeType( library );

  delete[] ucs4text;
}

void pngwriter::plot_text_utf8( char * face_path, const int & fontsize,
				const unsigned & x_start,
				const unsigned & y_start,
				const double & angle,  char * text,
				const int & red, const int & green,
				const int & blue)
{
  master_plot_text_utf8( face_path, fontsize, x_start, y_start, angle, text,
			 red, green, blue );
}

void pngwriter::plot_text( char * face_path, const int & fontsize,
			   const unsigned & x_start, const unsigned & y_start,
			   const double & angle, char * text,
			   const pngwriter::Byte & red, const pngwriter::Byte & green,
			   const pngwriter::Byte & blue)
{
  master_plot_text( face_path, fontsize, x_start, y_start,  angle,  text,
		    int( red ) << 8, int( green ) << 8, int( blue ) << 8  );
}

void pngwriter::plot_text( char * face_path, const int & fontsize,
			   const unsigned & x_start, const unsigned & y_start,
			   const double & angle, char * text,
			   const double & red, const double & green,
			   const double & blue)
{
  master_plot_text( face_path, fontsize, x_start, y_start,  angle, text,
		    int( red * 65535. ), int( green * 65535. ),
		    int( blue * 65535. ) );
}

void pngwriter::plot_text_utf8( char * face_path, const int & fontsize,
				const unsigned & x_start,
				const unsigned & y_start,
				const double & angle, char * text,
				const pngwriter::Byte & red, const pngwriter::Byte & green,
				const pngwriter::Byte & blue)
{
  master_plot_text_utf8( face_path, fontsize, x_start, y_start,  angle,  text,
			 int( red ) << 8, int( green ) << 8, int( blue ) << 8 );
}

void pngwriter::plot_text_utf8( char * face_path, const int & fontsize,
				const unsigned & x_start,
				const unsigned & y_start,
				const double & angle, char * text,
				const double & red, const double & green,
				const double & blue)
{
  master_plot_text_utf8( face_path, fontsize, x_start, y_start,  angle,  text,
			 int( red * 65535. ),  int( green * 65535. ),
			 int( blue * 65535. ) );
}



void pngwriter::master_my_draw_bitmap( FT_Bitmap * bitmap,
				       const unsigned & x, const unsigned & y,
				       const double & red,
				       const double & green,
				       const double & blue,
				       const double & opacity )
{
  double temp;

  bool blend( std::abs( opacity - 1 ) > 1.e-7 );

  for(int j=1; j<bitmap->rows+1; j++)
    {
      for(int i=1; i< bitmap->width + 1; i++)
	{
	  temp = (double)(bitmap->buffer[(j-1)*bitmap->width + (i-1)] )/255.0;

	  if(temp)
	    {
	      if ( blend )
		 plot_blend(x + i,
			    y  - j,
			    opacity,
			    temp*red + (1-temp)*(dread(x+i,y-j,tRed)),
			    temp*green + (1-temp)*(dread(x+i,y-j,tGreen)),
			    temp*blue + (1-temp)*(dread(x+i,y-j,tBlue))
			    );
	      else
		plot(x + i,
		     y  - j,
		     temp*red + (1-temp)*(dread(x+i,y-j,tRed)),
		     temp*green + (1-temp)*(dread(x+i,y-j,tGreen)),
		     temp*blue + (1-temp)*(dread(x+i,y-j,tBlue))
		     );
	    }
	}
    }
}

void pngwriter::my_draw_bitmap( FT_Bitmap * bitmap,
				const unsigned & x, const unsigned & y,
				const double & red,
				const double & green,
				const double & blue )
{
  master_my_draw_bitmap( bitmap, x, y, red, green, blue );
}


//////////// Get text width

//put in freetype section

int pngwriter::get_text_width(char * face_path, const int & fontsize,
			      char * text)
{
   
  FT_Library  library;
  FT_Face     face;
  FT_Matrix   matrix;      // transformation matrix
  FT_Vector   pen;

  FT_UInt glyph_index;
  FT_Error error;

  FT_Bool use_kerning;
  FT_UInt previous = 0;

  /* Set up transformation Matrix */
  matrix.xx = (FT_Fixed)( 1.0*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
  matrix.xy = (FT_Fixed)( 0.0*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
  matrix.yx = (FT_Fixed)( 0.0*0x10000);  //   matrix.yx = - matrix.xy;
  matrix.yy = (FT_Fixed)( 1.0*0x10000);  //   matrix.yy = matrix.xx;

  /* Place starting coordinates in adequate form. */
  pen.x = 0;
  pen.y = 0;

  /*Count the length of the string */
  int num_chars = strlen(text);

  /* Initialize FT Library object */
  error = FT_Init_FreeType( &library );
  if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not init Library."<< std::endl; return 0;}

  /* Initialize FT face object */
  error = FT_New_Face( library,face_path,0,&face );
  if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return 0; } else if (error){ std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not find or load font file." << std::endl; return 0; }

  /* Set the Char size */
  error = FT_Set_Char_Size( face,          /* handle to face object           */
			    0,             /* char_width in 1/64th of points  */
			    fontsize*64,   /* char_height in 1/64th of points */
			    100,           /* horizontal device resolution    */
			    100 );         /* vertical device resolution      */

  /* A way of accesing the glyph directly */
  FT_GlyphSlot  slot = face->glyph;  // a small shortcut

  /* Does the font file support kerning? */
  use_kerning = FT_HAS_KERNING( face );

  int n;
  for ( n = 0; n < num_chars; n++ )
    {
      /* Convert character code to glyph index */
      glyph_index = FT_Get_Char_Index( face, text[n] );

      /* Retrieve kerning distance and move pen position */
      if ( use_kerning && previous&& glyph_index )
	{
	  FT_Vector  delta;
	  FT_Get_Kerning( face,
			  previous,
			  glyph_index,
			  ft_kerning_default, //FT_KERNING_DEFAULT,
			  &delta );

	  /* Transform this kerning distance into rotated space */
	  pen.x += (int) ( delta.x);
	  pen.y +=  0;
	}

      /* Set transform */
      FT_Set_Transform( face, &matrix, &pen );

      /*set char size*/

      if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Set char size error." << std::endl; return 0;}

      /* Retrieve glyph index from character code */
      glyph_index = FT_Get_Char_Index( face, text[n] );

      /* Load glyph image into the slot (erase previous one) */
      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
      if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return 0;}

      /* Convert to an anti-aliased bitmap */
      //	error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
      error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
      if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Render glyph error." << std::endl; return 0;}

      /* Now, draw to our target surface */
      /*	my_draw_bitmap( &slot->bitmap,
		slot->bitmap_left,
		slot->bitmap_top,
		red,
		green,
		blue );
      */
      /* Advance to the next position */
      pen.x += slot->advance.x;
      //	std::cout << ((double) pen.x)/64.0 << std::endl;
      pen.y += slot->advance.y;

      /* record current glyph index */
      previous = glyph_index;
    }

   
  /* Free the face and the library objects */
  FT_Done_Face    ( face );
  FT_Done_FreeType( library );
   
  return (int)( ((double)pen.x)/64.0 );
}


int pngwriter::get_text_width_utf8(char * face_path, const int & fontsize,
				   char * text)
{
  FT_Library  library;
  FT_Face     face;
  FT_Matrix   matrix;      // transformation matrix
  FT_Vector   pen;

  FT_UInt glyph_index;
  FT_Error error;

  FT_Bool use_kerning;
  FT_UInt previous = 0;

  /* Set up transformation Matrix */
  matrix.xx = (FT_Fixed)( 0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
  matrix.xy = (FT_Fixed)( 0*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
  matrix.yx = (FT_Fixed)( 0*0x10000);  //   matrix.yx = - matrix.xy;
  matrix.yy = (FT_Fixed)( 0x10000);  //   matrix.yy = matrix.xx;

  /* Place starting coordinates in adequate form. */
  pen.x = 0 ;
  pen.y = 0;

  /*Count the length of the string */
  int num_bytes=0;
  while(text[num_bytes]!=0)
    {
      num_bytes++;
    }

  /*
    std::cout << "Num bytes is: "<< num_bytes << std::endl;
  */

  //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file.
  long * ucs4text;
  ucs4text = new long[num_bytes+1];

  Byte u,v,w,x,y,z;

  int num_chars=0;

  long iii=0;

  while(iii<num_bytes)
    {
      z = text[iii];

      if(z<=127)
	{
	  ucs4text[num_chars] = z;
	}

      if((192<=z)&&(z<=223))
	{
	  iii++; y = text[iii];
	  ucs4text[num_chars] = (z-192)*64 + (y -128);
	}

      if((224<=z)&&(z<=239))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  ucs4text[num_chars] = (z-224)*4096 + (y -128)*64 + (x-128);
	}

      if((240<=z)&&(z<=247))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  iii++; w = text[iii];
	  ucs4text[num_chars] = (z-240)*262144 + (y -128)*4096 + (x-128)*64 + (w-128);
	}

      if((248<=z)&&(z<=251))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  iii++; w = text[iii];
	  iii++; v = text[iii];
	  ucs4text[num_chars] = (z-248)*16777216 + (y -128)*262144 + (x-128)*4096 + (w-128)*64 +(v-128);
	}

      if((252==z)||(z==253))
	{
	  iii++; y = text[iii];
	  iii++; x = text[iii];
	  iii++; w = text[iii];
	  iii++; v = text[iii];
	  u = text[iii];
	  ucs4text[num_chars] = (z-252)*1073741824 + (y -128)*16777216   + (x-128)*262144 + (w-128)*4096 +(v-128)*64 + (u-128);
	}

      if((z==254)||(z==255))
	{
	  std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: Problem with character: invalid UTF-8 data."<< std::endl;
	}
      // std::cerr << "\nProblem at " << iii << ".\n";
      //
      iii++;
      num_chars++;
    }

  // num_chars now contains the number of characters in the string.
  /*
    std::cout << "Num chars is: "<< num_chars << std::endl;
  */

  /* Initialize FT Library object */
  error = FT_Init_FreeType( &library );
  if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Could not init Library."<< std::endl; return 0;}

  /* Initialize FT face object */
  error = FT_New_Face( library,face_path,0,&face );
  if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return 0; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return 0; }

  /* Set the Char size */
  error = FT_Set_Char_Size( face,          /* handle to face object           */
			    0,             /* char_width in 1/64th of points  */
			    fontsize*64,   /* char_height in 1/64th of points */
			    100,           /* horizontal device resolution    */
			    100 );         /* vertical device resolution      */

  /* A way of accesing the glyph directly */
  FT_GlyphSlot  slot = face->glyph;  // a small shortcut

  /* Does the font file support kerning? */
  use_kerning = FT_HAS_KERNING( face );

  int n;
  for ( n = 0; n < num_chars; n++ )
    {
      /* Convert character code to glyph index */
      glyph_index = FT_Get_Char_Index( face, ucs4text[n] );

      /* Retrieve kerning distance and move pen position */
      if ( use_kerning && previous&& glyph_index )
	{
	  FT_Vector  delta;
	  FT_Get_Kerning( face,
			  previous,
			  glyph_index,
			  ft_kerning_default, //FT_KERNING_DEFAULT,
			  &delta );

	  /* Transform this kerning distance into rotated space */
	  pen.x += (int) (delta.x);
	  pen.y +=  0;
	}

      /* Set transform */
      FT_Set_Transform( face, &matrix, &pen );

      /*set char size*/

      if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Set char size error." << std::endl; return 0;}

      /* Retrieve glyph index from character code */
      glyph_index = FT_Get_Char_Index( face, ucs4text[n] );

      /* Load glyph image into the slot (erase previous one) */
      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
      if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return 0;}

      /* Convert to an anti-aliased bitmap */
      error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
      if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Render glyph error." << std::endl; return 0;}

      /* Now, draw to our target surface */
      /*	my_draw_bitmap( &slot->bitmap,
		slot->bitmap_left,
		y_start + slot->bitmap_top,
		red,
		green,
		blue );
      */
      /* Advance to the next position */
      pen.x += slot->advance.x;
      pen.y += slot->advance.y;

      /* record current glyph index */
      previous = glyph_index;
    }

  /* Free the face and the library objects */
  FT_Done_Face    ( face );
  FT_Done_FreeType( library );

  delete[] ucs4text;
 
  return (int) (((double) pen.x)/64.0);
}

///////////////
#endif
#ifdef NO_FREETYPE

void pngwriter::plot_text( char * face_path, const int & fontsize,
			   const unsigned & x_start, const unsigned & y_start,
			   const double & angle, char * text,
			   const int & red, const int & green, const int & blue)
{
  std::cerr << " PNGwriter::plot_text - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

void pngwriter::plot_text( char * face_path, const int & fontsize,
			   const unsigned & x_start, const unsigned & y_start,
			   const double & angle, char * text,
			   const double & red, const double & green,
			   const double & blue)
{
  std::cerr << " PNGwriter::plot_text - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;

}

void pngwriter::plot_text_utf8( char * face_path, const int & fontsize,
				const unsigned & x_start,
				const unsigned & y_start, const double & angle,
				char * text, const int & red,
				const int & green, const int & blue)
{
  std::cerr << " PNGwriter::plot_text_utf8 - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

void pngwriter::plot_text_utf8( char * face_path, const int & fontsize,
				const unsigned & x_start,
				const unsigned & y_start, const double & angle,
				char * text, const double & red,
				const double & green, const double & blue)
{
  std::cerr << " PNGwriter::plot_text_utf8 - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

//////////// Get text width
int pngwriter::get_text_width(char * face_path, const int & fontsize,
			      char * text)
{
  std::cerr << " PNGwriter::get_text_width - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return 0;
}


int pngwriter::get_text_width_utf8(char * face_path, const int & fontsize,
				   char * text)
{
  std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return 0;
}

///////////////
#endif

/////////////////////////////////////
int pngwriter::bilinear_interpolation_read(const double & x, const double & y,
					   const RGBColours & colour)
{
  if ( x < -1.e-7 || y < -1.e-7 )
    return 0;

  double iPartX, iPartY,fPartX, fPartY;
  fPartX = std::modf( x, &iPartX );
  fPartY = std::modf( y, &iPartY );

  unsigned inty( iPartY ), intx( iPartX );

  if ( intx >= width_ || inty >= height_ )
    return 0;

  // std::cout << "(x, y) = (" << x << ", " << y << "), => (" << intx
  // 	    << " + " << fPartX << ", " << inty << " + " << fPartY << ")" << std::endl;

  bool attop( inty == height_ - 1 ), atright( intx == width_ - 1 );

  double f,g,f1,g1;
  f = fPartX;
  g = fPartY;
  f1 = 1. - fPartX;
  g1 = 1. - fPartY;

  if ( ! attop && ! atright )
    {

      double result( f1 * g1 * dread( intx    , inty    , colour) +
		     f  * g1 * dread( intx + 1, inty    , colour) +
		     f1 *  g * dread( intx    , inty + 1, colour) +
		     f  *  g * dread( intx + 1, inty + 1, colour) );

      return int( result * 65535. );
    }
  else if( (atright)&&(!attop))
    {

      return (int) (
		    f1*g1*read(intx, inty,colour)
		    + f*g1*( 2*(read(intx,inty,colour)) - (read(intx-1,inty,colour)) )
		    +f1*g*read(intx,inty+1,colour)
		    + f*g*(2*(read(intx,inty+1,colour)) - (read(intx-1,inty+1,colour)))
		    );
    }
  else if((attop)&&(!atright))
    {

      return int( f1 * g1 * read( intx, inty, colour )
		  + f * g1 * read( intx+1, inty, colour )
		  + f1 * g * ( 2 * ( read( intx, inty, colour ) )  - read( intx, inty-1, colour ) )
		  + f * g * ( 2 * ( read( intx+1, inty, colour ) )  -
			      read( intx+1, inty-1, colour ) ) );
    }
  
  return int( f1*g1*read(intx, inty,colour)
	      + f*g1*( 2*(read(intx,inty,colour)) - (read(intx-1,inty,colour)) )
	      +f1*g*( 2*(read(intx,inty,colour))  - read(intx,inty-1,colour) )
	      + f*g*( 2*( 2*(read(intx,inty,colour)) - (read(intx-1,inty,colour)) )
		      - ( 2*(read(intx,inty-1,colour)) - (read(intx-1,inty-1,colour)) ))
	      );
  
  /*
    return (int) (
    f1*g1*read(intx, inty,colour)
    + f*g1*read(intx+1,inty,colour)
    +f1*g*read(intx,inty+1,colour)
    + f*g*read(intx+1, inty+1,colour)
    );
    * */

}

pngwriter::Byte pngwriter::bilinear_interpolation_cread(const double & x, const double & y,
					     const RGBColours & colour)
{
  int integer( bilinear_interpolation_read(x,y,colour) );

  return pngwriter::Byte( integer >> 8 );
}

double pngwriter::bilinear_interpolation_dread(const double & x,
					       const double & y,
					       const RGBColours & colour)
{
  return double( bilinear_interpolation_read( x, y, colour ) ) / 65535. ;
}

void pngwriter::plot_blend(const unsigned & x, const unsigned & y,
			   const double & opacity,
			   const int & red, const int & green, const int & blue)
{
  unsigned lOpacity( std::min( 256u,
			       std::max( 0u, unsigned( opacity * 256 ) ) ) );

  int
    lRed(   lOpacity * red   + read( x, y, tRed )   * ( 256 - lOpacity ) ),
    lGreen( lOpacity * green + read( x, y, tGreen ) * ( 256 - lOpacity ) ),
    lBlue(  lOpacity * blue  + read( x, y, tBlue )  * ( 256 - lOpacity ) );
  
  plot( x, y, lRed >> 8, lGreen >> 8, lBlue >> 8 );
}

void pngwriter::plot_blend(const unsigned & x, const unsigned & y,
			   const double & opacity, const pngwriter::Byte & red,
			   const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  plot_blend( x, y, opacity, int( red ) << 8, int( green ) << 8,
	      int( blue ) << 8 );
}

void pngwriter::plot_blend(const unsigned & x, const unsigned & y,
			   const double & opacity, const double & red,
			   const double & green, const double & blue)
{
  plot_blend( x, y, opacity, int( red * 65535. ), int( green * 65535. ),
	      int( blue * 65535. ) );
}

void pngwriter::invert(void)
{
  //   int temp1, temp2, temp3;
  int temp11, temp22, temp33;
  unsigned iii, jjj;

  for( jjj = 1; jjj <= (height_); jjj++)
    {
      for( iii = 1; iii <= (width_); iii++)
	{
	  temp11 = read( iii, jjj, tRed );
	  temp22 = read( iii, jjj, tGreen );
	  temp33 = read( iii, jjj, tBlue );

	  plot( iii, jjj, 65535 - temp11, 65535 - temp22, 65535 - temp33 );
	}
    }
}

void pngwriter::resize(const int & width, const int & height)
{

  for (unsigned jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
  free(graph_);

  width_ = width;
  height_ = height;
  backgroundcolour_ = 0;

  if ( init_pixel_matrix() > 0 )
    std::cerr << " PNGwriter::resize - ERROR **: cannot create pixel matrix"
	      << std::endl;
}

void pngwriter::master_boundary_fill(const unsigned & xstart,
				     const unsigned & ystart,
				     const int & boundary_red,
				     const int & boundary_green,
				     const int & boundary_blue,
				     const int & fill_red,
				     const int & fill_green,
				     const int & fill_blue,
				     const double & opacity)
{
  if( (
       read(xstart,ystart, tRed ) != boundary_red ||
       read(xstart,ystart, tGreen ) != boundary_green ||
       read(xstart,ystart, tBlue ) != boundary_blue
       )
      &&
      (
       read(xstart,ystart, tRed ) != fill_red ||
       read(xstart,ystart, tGreen ) != fill_green ||
       read(xstart,ystart, tBlue ) != fill_blue
       )
      &&
      xstart <= width_ && ystart <= height_ )
    {
      if ( std::abs( opacity - 1. ) < 1.e-7 )
	plot( xstart, ystart, fill_red, fill_green, fill_blue );
      else
	plot_blend( xstart, ystart, opacity, fill_red, fill_green, fill_blue );
      boundary_fill(xstart+1,  ystart,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
      boundary_fill(xstart,  ystart+1,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
      boundary_fill(xstart,  ystart-1,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
      boundary_fill(xstart-1,  ystart,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
    }
}

void pngwriter::boundary_fill(const unsigned & xstart, const unsigned & ystart,
			      const int & boundary_red,
			      const int & boundary_green,
			      const int & boundary_blue,
			      const int & fill_red, const int & fill_green,
			      const int & fill_blue)
{
  master_boundary_fill( xstart, ystart,
			boundary_red, boundary_green, boundary_blue,
			fill_red, fill_green, fill_blue );
}
//no int version needed

void pngwriter::master_flood_fill_internal(const unsigned & xstart,
					   const unsigned & ystart,
					   const int & start_red,
					   const int & start_green,
					   const int & start_blue,
					   const int & fill_red,
					   const int & fill_green,
					   const int & fill_blue,
					   const double & opacity)
{
  if( (
       read(xstart,ystart, tRed ) == start_red &&
       read(xstart,ystart, tGreen ) == start_green &&
       read(xstart,ystart, tBlue ) == start_blue
       )
      &&
      (
       read(xstart,ystart, tRed ) != fill_red ||
       read(xstart,ystart, tGreen ) != fill_green ||
       read(xstart,ystart, tBlue ) != fill_blue
       )
      &&
      xstart <= width_ && ystart <= height_
      )
    {
      if ( std::abs( opacity - 1. ) < 1.e-7 )
	plot(xstart, ystart, fill_red, fill_green, fill_blue);
      else
	plot_blend( xstart, ystart, opacity,
		    fill_red, fill_green, fill_blue);
      master_flood_fill_internal( xstart+1,  ystart,
				  start_red, start_green, start_blue,
				  fill_red, fill_green, fill_blue, opacity);
      master_flood_fill_internal( xstart-1, ystart,
				  start_red, start_green, start_blue,
				  fill_red, fill_green, fill_blue, opacity);
      master_flood_fill_internal( xstart, ystart+1,
				  start_red, start_green, start_blue,
				  fill_red, fill_green, fill_blue, opacity);
      master_flood_fill_internal( xstart, ystart-1,
				  start_red, start_green, start_blue,
				  fill_red, fill_green, fill_blue, opacity);
    }

}

void pngwriter::flood_fill_internal(const unsigned & xstart,
				    const unsigned & ystart,
				    const int & start_red,
				    const int & start_green,
				    const int & start_blue,
				    const int & fill_red,
				    const int & fill_green,
				    const int & fill_blue)
{
  master_flood_fill_internal( xstart, ystart,
			      start_red, start_green, start_blue,
			      fill_red, fill_green, fill_blue );
}

// Byte
void pngwriter::boundary_fill(const unsigned & xstart, const unsigned & ystart,
			      const pngwriter::Byte & boundary_red,
			      const pngwriter::Byte & boundary_green,
			      const pngwriter::Byte & boundary_blue,
			      const pngwriter::Byte & fill_red, const pngwriter::Byte & fill_green,
			      const pngwriter::Byte & fill_blue)
{
  master_boundary_fill( xstart, ystart,
			int( boundary_red) << 8, int( boundary_green ) << 8,
			int( boundary_blue ) << 8,
			int( fill_red ) << 8, int( fill_green ) << 8,
			int( fill_blue ) << 8 );
}

//int version
void pngwriter::boundary_fill(const unsigned & xstart, const unsigned & ystart,
			      const double & boundary_red,
			      const double & boundary_green,
			      const double & boundary_blue,
			      const double & fill_red, const double & fill_green,
			      const double & fill_blue)
{

  master_boundary_fill( xstart, ystart,
			int( boundary_red * 65535. ),
			int( boundary_green * 65535. ),
			int( boundary_blue * 65535. ),
			int( fill_red * 65535. ),
			int( fill_green * 65535. ),
			int( fill_blue * 65535. ) );
}

void pngwriter::master_flood_fill(const unsigned & xstart,
				  const unsigned & ystart,
				  const int & fill_red,
				  const int & fill_green,
				  const int  & fill_blue,
				  const double & opacity)
{
  master_flood_fill_internal( xstart, ystart,
			      read( xstart, ystart, tRed ),
			      read( xstart, ystart, tGreen ),
			      read( xstart, ystart, tBlue ),
			      fill_red, fill_green, fill_blue,
			      opacity );
}

void pngwriter::flood_fill(const unsigned & xstart,
			   const unsigned & ystart,
			   const int & fill_red, const int & fill_green,
			   const int & fill_blue)
{
  master_flood_fill( xstart, ystart, fill_red, fill_green, fill_blue );
}


void pngwriter::flood_fill(const unsigned & xstart, const unsigned & ystart,
			   const pngwriter::Byte & fill_red, const pngwriter::Byte & fill_green,
			   const pngwriter::Byte & fill_blue)
{
  master_flood_fill( xstart,  ystart, int( fill_red ) << 8,
		     int( fill_green ) << 8, int( fill_blue ) << 8 );
}

//int version
void pngwriter::flood_fill(const unsigned & xstart, const unsigned & ystart,
			   const double & fill_red, const double & fill_green,
			   const double & fill_blue)
{
  master_flood_fill( xstart,  ystart, int( fill_red * 65535. ) ,
		     int( fill_green * 65535. ), int( fill_blue * 65535. ) );
}

void pngwriter::master_polygon( unsigned * points,
				const unsigned & number_of_points,
				const int & red, const int & green,
				const int & blue, const double & opacity)
{
  if( number_of_points<1 || points == 0 )
    {
      std::cerr << " PNGwriter::master_polygon - ERROR **:  Number of points is zero or negative, or array is 0." << std::endl;
      return;
    }

  for(unsigned k( 0 );k< number_of_points-1; k++)
    {
      master_line( points[2*k], points[2*k+1],
		   points[2*k+2], points[2*k+3], red, green, blue, opacity );
    }
}

void pngwriter::polygon( unsigned * points, const unsigned & number_of_points,
			 const int & red, const int & green, const int & blue )
{
  master_polygon( points, number_of_points, red, green, blue );
}

//Byte version
void pngwriter::polygon( unsigned * points, const unsigned & number_of_points,
			 const pngwriter::Byte & red, const pngwriter::Byte & green,
			 const pngwriter::Byte & blue)
{
  master_polygon( points, number_of_points, int( red ) << 8, int( green ) << 8,
		  int( blue ) << 8 );
}

//int version
void pngwriter::polygon( unsigned * points, const unsigned & number_of_points,
			 const double & red, const double & green,
			 const double & blue)
{
  master_polygon( points, number_of_points, int( red * 65535. ),
		  int( green * 65535. ), int( blue * 65535. ) );
}

void pngwriter::master_plotCMYK(const unsigned & x, const unsigned & y,
				const double & cyan, const double & magenta,
				const double & yellow, const double & black,
				const double & opacity)
{
  /*CMYK to RGB:
   *  -----------
   *  red   = 255 - minimum(255,((cyan/255)    * (255 - black) + black))
   *  green = 255 - minimum(255,((magenta/255) * (255 - black) + black))
   *  blue  = 255 - minimum(255,((yellow/255)  * (255 - black) + black))
   * */

  double
    localC( std::min( 1., std::max( cyan, 0. ) ) ),
    localM( std::min( 1., std::max( magenta, 0. ) ) ),
    localY( std::min( 1., std::max( yellow, 0. ) ) ),
    localB( std::min( 1., std::max( black, 0. ) ) );

  double minr, ming, minb, iblack;

  iblack = 1.0 - localB;

  minr = 1.0;
  ming = 1.0;
  minb = 1.0;

  if( (localC*iblack + localB)<1.0 )
    {
      minr = localC*iblack + localB;
    }

  if( (localM*iblack + localB)<1.0 )
    {
      ming = localM*iblack + localB;
    }

  if( (localY*iblack + localB)<1.0 )
    {
      minb = localY*iblack + localB;
    }

  if ( std::abs( opacity - 1. ) < 1.e-7 )
    plot( x, y, 1. - minr, 1. - ming, 1. - minb );
  else
    plot_blend( x, y, opacity, 1. - minr, 1. - ming, 1. - minb );
}

void pngwriter::plotCMYK(const unsigned & x, const unsigned & y,
			 const double & cyan, const double & magenta,
			 const double & yellow, const double & black)
{
  master_plotCMYK( x, y, cyan, magenta, yellow, black );
}

//Byte version
void pngwriter::plotCMYK(const unsigned & x, const unsigned & y,
			 const pngwriter::Byte & cyan, const pngwriter::Byte & magenta,
			 const pngwriter::Byte & yellow, const pngwriter::Byte & black)
{
  master_plotCMYK( x, y, double( cyan ) / 255., double( magenta ) / 255.,
		   double( yellow ) / 255., double( black ) / 255. );
}

//int version
void pngwriter::plotCMYK(const unsigned & x, const unsigned & y,
			 const int & cyan, const int & magenta,
			 const int & yellow, const int & black)
{
  master_plotCMYK( x, y, double( cyan ) / 65535., double( magenta ) / 65535.,
		   double( yellow ) / 65535., double( black ) / 65535. );
}

double pngwriter::dreadCMYK(const unsigned & x, const unsigned & y,
			    const CMYKColours & colour)
{
  /*
   * Black   = minimum(1-Red,1-Green,1-Blue)
   *     Cyan    = (1-Red-Black)/(1-Black)
   *     Magenta = (1-Green-Black)/(1-Black)
   *     Yellow  = (1-Blue-Black)/(1-Black)
   *
   * */

  double black, ired, igreen, iblue, iblack;
  //add error detection here
  // not much to detect, really
  ired   = 1. - dread( x, y, tRed );
  igreen = 1. - dread( x, y, tGreen );
  iblue  = 1. - dread( x, y, tBlue );

  //black is the mimimum of inverse RGB colours, and if they are all equal, it is the inverse of red.
  black = std::min( ired, std::min( igreen, iblue ) );

  iblack = 1.0 - black;

  switch ( colour )
    {
    case tCyan:
      return ((ired-black)/(iblack));
      break;
    case tMagenta:
      return ((igreen-black)/(iblack));
      break;
    case tYellow:
      return ((iblue-black)/(iblack));
      break;
    case tBlack:
      return black;
      break;
    default:
      std::cerr << " PNGwriter::dreadCMYK - WARNING **: Invalid argument: should be 1, 2, 3 or 4, is " << colour << std::endl;
      return 0.;
    }
}

int pngwriter::readCMYK(const unsigned & x, const unsigned & y,
			const CMYKColours & colour)
{
  /*
   * Black   = minimum(1-Red,1-Green,1-Blue)
   *     Cyan    = (1-Red-Black)/(1-Black)
   *     Magenta = (1-Green-Black)/(1-Black)
   *     Yellow  = (1-Blue-Black)/(1-Black)
   *
   * */

  double dVal( dreadCMYK( x, y, colour ) );

  return int( dVal * 65535. );

  /*
    if((colour !=1)&&(colour !=2)&&(colour !=3)&&(colour !=4))
    {
    std::cerr << " PNGwriter::readCMYK - WARNING **: Invalid argument: should be 1, 2, 3 or 4, is " << colour << std::endl;
    return 0;
    }

    double black, red, green, blue, ired, igreen, iblue, iblack;
    //add error detection here
    // not much to detect, really
    red = dread(x, y, 1);
    green = dread(x, y, 2);
    blue = dread(x, y, 3);

    ired = 1.0 - red;
    igreen = 1.0 - green;
    iblue = 1.0 - blue;

    black = ired;

    //black is the mimimum of inverse RGB colours, and if they are all equal, it is the inverse of red.
    if( (igreen<ired)&&(igreen<iblue) )
    {
    black = igreen;
    }

    if( (iblue<igreen)&&(iblue<ired) )
    {
    black = iblue;
    }

    iblack = 1.0 - black;

    if(colour == 1)
    {
    return (int)( ((ired-black)/(iblack))*65535);
    }

    if(colour == 2)
    {
    return (int)( ((igreen-black)/(iblack))*65535);
    }

    if(colour == 3)
    {
    return (int)( ((iblue-black)/(iblack))*65535);
    }

    if(colour == 4)
    {
    return (int)( (black)*65535);
    }

    return 0;
  */
}

pngwriter::Byte pngwriter::creadCMYK(const unsigned & x,
			  const unsigned & y,
			  const CMYKColours & colour)
{
  /*
   * Black   = minimum(1-Red,1-Green,1-Blue)
   *     Cyan    = (1-Red-Black)/(1-Black)
   *     Magenta = (1-Green-Black)/(1-Black)
   *     Yellow  = (1-Blue-Black)/(1-Black)
   *
   * */

  double dVal( dreadCMYK( x, y, colour ) );

  return int( dVal * 255. );
}

void pngwriter::scale_k(const double & k)
{
  if( k < 1.e-7 )
    {
      std::cerr << " PNGwriter::scale_k - ERROR **:  scale_k() called with negative or zero scale factor. Was: " << k << "." << std::endl;
    }

  if ( std::abs( k - 1. ) < 1.e-7 )
    return;

  // Calculate the new scaled height and width
  unsigned scaledh, scaledw;
  scaledw = unsigned( k * width_ );
  scaledh = unsigned( k * height_ );

  // Create image storage.
  pngwriter temp( scaledw, scaledh, 0, "temp.png", bit_depth_ );

  int red, green, blue;

  double readx( 0. ), ready( 0. );

  unsigned x, y;

  for(x = 0; x < scaledw; x++)
    {
      for(y = 0; y < scaledh; y++)
	{
	  readx = double( x ) / k;
	  ready = double( y ) / k;
	  red   = bilinear_interpolation_read( readx, ready, tRed );
	  green = bilinear_interpolation_read( readx, ready, tGreen );
	  blue  = bilinear_interpolation_read( readx, ready, tBlue );
	  temp.plot( x, y, red, green, blue);
	}
    }

  // From here on, the process is the same for all scale functions.
  //Get data out of temp and into this's storage.

  //(*this) = temp;
  //Resize this instance
  // Delete current storage.
  for (unsigned jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
  free(graph_);

  //New image will have bit depth 16, regardless of original bit depth.

  // New width and height will be the scaled width and height
  width_ = scaledw;
  height_ = scaledh;
  backgroundcolour_ = 0;

  if ( init_pixel_matrix( temp.graph_ ) > 0 )
     std::cerr << " PNGwriter::pngwriter - ERROR **: cannot initialise the class"
	       << std::endl;

  return;

  // graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
  // if(graph_ == 0)
  //   {
  //     std::cerr << " PNGwriter::scale_k - ERROR **:  Not able to allocate memory for image." << std::endl;
  //   }

  // for (int kkkk = 0; kkkk < height_; kkkk++)
  //   {
  //     graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
  //     if(graph_[kkkk] == 0)
  // 	{
  // 	  std::cerr << " PNGwriter::scale_k - ERROR **:  Not able to allocate memory for image." << std::endl;
  // 	}
  //   }

  // if(graph_ == 0)
  //   {
  //     std::cerr << " PNGwriter::scale_k - ERROR **:  Not able to allocate memory for image." << std::endl;
  //   }

  //This instance now has a new, resized storage space.

  //Copy the temp date into this's storage.
   unsigned tempindex, hhh, vhhh;
   if ( bit_depth_ == 16 )
     {
       for( hhh = 0; hhh < width_; hhh++ )
	 {
	   for( vhhh = 0; vhhh < height_; vhhh++ )
	     {
	       tempindex=6*hhh;
	       graph_[vhhh][tempindex]   = temp.graph_[vhhh][tempindex];
	       graph_[vhhh][tempindex+1] = temp.graph_[vhhh][tempindex+1];
	       graph_[vhhh][tempindex+2] = temp.graph_[vhhh][tempindex+2];
	       graph_[vhhh][tempindex+3] = temp.graph_[vhhh][tempindex+3];
	       graph_[vhhh][tempindex+4] = temp.graph_[vhhh][tempindex+4];
	       graph_[vhhh][tempindex+5] = temp.graph_[vhhh][tempindex+5];
	     }
	 }
     }
   else
     {
       for( hhh = 0; hhh < width_; hhh++ )
	 {
	   for( vhhh = 0; vhhh < height_; vhhh++ )
	     {
	       tempindex=3*hhh;
	       graph_[vhhh][tempindex] = temp.graph_[vhhh][tempindex];
	       graph_[vhhh][tempindex+1] = temp.graph_[vhhh][tempindex+1];
	       graph_[vhhh][tempindex+2] = temp.graph_[vhhh][tempindex+2];
	     }
	 }
     }
  // this should now contain the new, scaled image data.
  //
}

void pngwriter::scale_kxky(double kx, double ky)
{
  if((kx <= 0.0)||(ky <= 0.0))
    {
      std::cerr << " PNGwriter::scale_kxky - ERROR **:  scale_kxky() called with negative or zero scale factor. Was: " << kx << ", " << ky << "." << std::endl;
    }

  int scaledh, scaledw;
  scaledw = (int) ceil(kx*width_);
  scaledh = (int) ceil(ky*height_);

  pngwriter temp(scaledw, scaledh, 0, "temp");

  int red, green, blue;

  double spacingx = ((double)width_)/(2*scaledw);
  double spacingy = ((double)height_)/(2*scaledh);

  double readx, ready;

  for(int x = 1; x<= scaledw; x++)
    {
      for(int y = 1; y <= scaledh; y++)
	{
	  readx = (2*x-1)*spacingx;
	  ready = (2*y-1)*spacingy;
	  red = bilinear_interpolation_read(readx, ready,  tRed );
	  green = bilinear_interpolation_read(readx, ready, tGreen );
	  blue = bilinear_interpolation_read(readx, ready, tBlue );
	  temp.plot(x, y, red, green, blue);
	}
    }
  // From here on, the process is the same for all scale functions.
  //Get data out of temp and into this's storage.

  //Resize this instance
  // Delete current storage.
  for (unsigned jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
  free(graph_);

  // New width and height will be the scaled width and height
  width_ = scaledw;
  height_ = scaledh;
  backgroundcolour_ = 0;

  if ( init_pixel_matrix( temp.graph_ ) > 0 )
    std::cerr << " PNGwriter::pngwriter - ERROR **: cannot resize pixel matrix"
	      << std::endl;
  /*
    graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
    if(graph_ == 0)
    {
    std::cerr << " PNGwriter::scale_kxky - ERROR **:  Not able to allocate memory for image." << std::endl;
    }

    for (int kkkk = 0; kkkk < height_; kkkk++)
    {
    graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
    if(graph_[kkkk] == 0)
    {
    std::cerr << " PNGwriter::scale_kxky - ERROR **:  Not able to allocate memory for image." << std::endl;
    }
    }

    if(graph_ == 0)
    {
    std::cerr << " PNGwriter::scale_kxky - ERROR **:  Not able to allocate memory for image." << std::endl;
    }

    //This instance now has a new, resized storage space.

    //Copy the temp date into this's storage.
    int tempindex;
    for(int hhh = 0; hhh<width_;hhh++)
    {
    for(int vhhh = 0; vhhh<height_;vhhh++)
    {
    tempindex=6*hhh;
    graph_[vhhh][tempindex] = temp.graph_[vhhh][tempindex];
    graph_[vhhh][tempindex+1] = temp.graph_[vhhh][tempindex+1];
    graph_[vhhh][tempindex+2] = temp.graph_[vhhh][tempindex+2];
    graph_[vhhh][tempindex+3] = temp.graph_[vhhh][tempindex+3];
    graph_[vhhh][tempindex+4] = temp.graph_[vhhh][tempindex+4];
    graph_[vhhh][tempindex+5] = temp.graph_[vhhh][tempindex+5];
    }
    }
  */
  // this should now contain the new, scaled image data.
  //
  //
}

void pngwriter::scale_wh(const int & finalwidth, const int & finalheight)
{
  if((finalwidth <= 0)||(finalheight <= 0))
    {
      std::cerr << " PNGwriter::scale_wh - ERROR **: Negative or zero final width or height not allowed." << std::endl;
    }

  double kx;
  double ky;

  kx = ((double)finalwidth)/((double)width_);
  ky = ((double)finalheight)/((double)height_);

  pngwriter temp(finalwidth, finalheight, 0, "temp");

  int red, green, blue;

  double spacingx = ((double)width_)/(2*finalwidth);
  double spacingy = ((double)height_)/(2*finalheight);

  double readx, ready;

  for(int x = 1; x<= finalwidth; x++)
    {
      for(int y = 1; y <= finalheight; y++)
	{
	  readx = (2*x-1)*spacingx;
	  ready = (2*y-1)*spacingy;
	  red = bilinear_interpolation_read(readx, ready, tRed );
	  green = bilinear_interpolation_read(readx, ready, tGreen );
	  blue = bilinear_interpolation_read(readx, ready, tBlue );
	  temp.plot(x, y, red, green, blue);

	}
    }

  // From here on, the process is the same for all scale functions.
  //Get data out of temp and into this's storage.

  //Resize this instance
  // Delete current storage.
  for (unsigned jjj( 0 ); jjj < height_; jjj++) free(graph_[jjj]);
  free(graph_);

  // New width and height will be the scaled width and height
  width_ = finalwidth;
  height_ = finalheight;
  backgroundcolour_ = 0;

  init_pixel_matrix();
  /*
    graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
    if(graph_ == 0)
    {
    std::cerr << " PNGwriter::scale_wh - ERROR **:  Not able to allocate memory for image." << std::endl;
    }
    
    for (unsigned kkkk( 0 ); kkkk < height_; kkkk++)
    {
    graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
    if(graph_[kkkk] == 0)
    {
    std::cerr << " PNGwriter::scale_wh - ERROR **:  Not able to allocate memory for image." << std::endl;
    }
    }
	
    if(graph_ == 0)
    {
    std::cerr << " PNGwriter::scale_wh - ERROR **:  Not able to allocate memory for image." << std::endl;
    }
	
    //This instance now has a new, resized storage space.
	
    //Copy the temp date into this's storage.
    unsigned hhh, vhhh, tempindex;
    for(hhh = 0; hhh<width_;hhh++)
    {
    for(vhhh = 0; vhhh<height_;vhhh++)
    {
    tempindex=6*hhh;
    graph_[vhhh][tempindex] = temp.graph_[vhhh][tempindex];
    graph_[vhhh][tempindex+1] = temp.graph_[vhhh][tempindex+1];
    graph_[vhhh][tempindex+2] = temp.graph_[vhhh][tempindex+2];
    graph_[vhhh][tempindex+3] = temp.graph_[vhhh][tempindex+3];
    graph_[vhhh][tempindex+4] = temp.graph_[vhhh][tempindex+4];
    graph_[vhhh][tempindex+5] = temp.graph_[vhhh][tempindex+5];
    }
    }
  */	  
  // this should now contain the new, scaled image data.
  //
  // 
}

// Blended functions
//
void pngwriter::plotHSV_blend(const unsigned & x, const unsigned & y,
			      const double & opacity, const double & hue,
			      const double & saturation, const double & value)
{
  double red,green,blue;
  // double *redp;
  // double *greenp;
  // double *bluep;

  // redp = &red;
  // greenp = &green;
  // bluep = &blue;

  HSVtoRGB(red,green,blue,hue,saturation,value);
  plot_blend(x,y,opacity, red,green,blue);

}

void pngwriter::plotHSV_blend(const unsigned & x, const unsigned & y,
			      const double & opacity, const pngwriter::Byte & hue,
			      const pngwriter::Byte & saturation, const pngwriter::Byte & value)
{
  plotHSV_blend(x, y, opacity,
		double( hue ) / 255.,
		double( saturation ) / 255.,
		double( value ) / 255. );
}

void pngwriter::plotHSV_blend(const unsigned & x, const unsigned & y,
			      const double & opacity, const int & hue,
			      const int & saturation, const int & value)
{
  plotHSV_blend( x, y, opacity, double( hue ) / 65535.,
		 double( saturation ) / 65535.,
		 double( value ) / 65535. );
}

void pngwriter::line_blend(const unsigned & xfrom, const unsigned & yfrom,
			   const unsigned & xto, const unsigned & yto,
			   const double & opacity,
			   const int & red, const int & green, const int & blue)
{
  master_line( xfrom, yfrom, xto, yto, red, green, blue, opacity );
  /*
  //  Bresenham Algorithm.
  //
  int dy = yto - yfrom;
  int dx = xto - xfrom;
  int stepx, stepy;

  if (dy < 0)
    {
      dy = -dy;  stepy = -1;
    }
  else
    {
      stepy = 1;
    }

  if (dx < 0)
    {
      dx = -dx;  stepx = -1;
    }
  else
    {
      stepx = 1;
    }
  dy <<= 1;     // dy is now 2*dy
  dx <<= 1;     // dx is now 2*dx

  plot_blend(xfrom,yfrom,opacity, red,green,blue);

  unsigned x(xfrom), y(yfrom);

  if (dx > dy)
    {
      int fraction = dy - (dx >> 1);

      while (x != xto)
	{
	  if (fraction >= 0)
	    {
	      y += stepy;
	      fraction -= dx;
	    }
	  x += stepx;
	  fraction += dy;
	  plot_blend(x,y,opacity, red,green,blue);
	}
    }
  else
    {
      int fraction = dx - (dy >> 1);
      while (y != yto)
	{
	  if (fraction >= 0)
	    {
	      x += stepx;
	      fraction -= dy;
	    }
	  y += stepy;
	  fraction += dx;
	  plot_blend(x,y, opacity, red,green,blue);
	}
    }
  */
}

void pngwriter::line_blend(const unsigned & xfrom, const unsigned & yfrom,
			   const unsigned & xto, const unsigned & yto,
			   const double & opacity, const pngwriter::Byte & red,
			   const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_line( xfrom, yfrom,
		     xto, yto,
		     int( red ) << 8,
		     int( green ) << 8,
		     int( blue ) << 8,
		     opacity );
}

void pngwriter::line_blend(const unsigned & xfrom, const unsigned & yfrom,
			   const unsigned & xto, const unsigned & yto,
			   const double & opacity, const double & red,
			   const double & green, const double & blue)
{
  master_line( xfrom,
	       yfrom,
	       xto,
	       yto,
	       int( red * 65535 ),
	       int( green * 65535 ),
	       int( blue * 65535 ),
	       opacity );
}

void pngwriter::square_blend(const unsigned & xfrom, const unsigned & yfrom,
			     const unsigned & xto, const unsigned & yto,
			     const double & opacity, const int & red,
			     const int & green,const int &  blue)
{
  master_square( xfrom, yfrom, xto, yto, red, green, blue, opacity );
}

void pngwriter::square_blend(const unsigned & xfrom, const unsigned & yfrom,
			     const unsigned & xto, const unsigned & yto,
			     const double & opacity, const pngwriter::Byte & red,
			     const pngwriter::Byte & green,const pngwriter::Byte & blue)
{
  master_square( xfrom,  yfrom,
		 xto,  yto,
		 int( red ) << 8,
		 int( green ) << 8,
		 int( blue ) << 8,
		 opacity );
}

void pngwriter::square_blend(const unsigned & xfrom, const unsigned & yfrom,
			     const unsigned & xto, const unsigned & yto,
			     const double & opacity, const double & red,
			     const double & green,const double & blue)
{
  master_square( xfrom,  yfrom,
		 xto,  yto,
		 int( red * 65535 ),
		 int( green * 65535 ),
		 int( blue * 65535 ),
		 opacity );
}

void pngwriter::filledsquare_blend(const unsigned & xfrom,
				   const unsigned & yfrom,
				   const unsigned & xto, const unsigned & yto,
				   const double & opacity, const int & red,
				   const int & green,const int &  blue)
{
  master_filledsquare( xfrom, yfrom, xto, yto, red, green, blue, opacity );
}

void pngwriter::filledsquare_blend(const unsigned & xfrom,
				   const unsigned & yfrom,
				   const unsigned & xto, const unsigned & yto,
				   const double & opacity, const pngwriter::Byte & red,
				   const pngwriter::Byte & green,const pngwriter::Byte & blue)
{
  master_filledsquare( xfrom, yfrom,
		       xto, yto,
		       int( red ) << 8,
		       int( green ) << 8,
		       int( blue ) << 8,
		       opacity );
}

void pngwriter::filledsquare_blend(const unsigned & xfrom,
				   const unsigned & yfrom,
				   const unsigned & xto, const unsigned & yto,
				   const double & opacity, const double & red,
				   const double & green,const double & blue)
{
  master_filledsquare( xfrom, yfrom,
		       xto, yto,
		       int( red * 65535. ),
		       int( green * 65535. ),
		       int( blue * 65535. ),
		       opacity );
}

void pngwriter::circle_aux_blend(const unsigned & xcentre,
				 const unsigned & ycentre,
				 const unsigned & x, const unsigned & y, 
				 const double & opacity, const int & red,
				 const int & green, const int & blue)
{
  master_circle_aux( xcentre, ycentre, x, y, red, green, blue, opacity );
  /*
  if (x == 0)
    {
      plot_blend( xcentre, ycentre + y, opacity, red, green, blue);
      plot_blend( xcentre, ycentre - y, opacity, red, green, blue);
      plot_blend( xcentre + y, ycentre, opacity, red, green, blue);
      plot_blend( xcentre - y, ycentre, opacity, red, green, blue);
    }
  else
    if (x == y)
      {
	plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue);
	plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue);
	plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue);
	plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue);
      }
    else
      if (x < y)
	{
	  plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue);
	  plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue);
	  plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue);
	  plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue);
	  plot_blend( xcentre + y, ycentre + x, opacity, red, green, blue);
	  plot_blend( xcentre - y, ycentre + x, opacity, red, green, blue);
	  plot_blend( xcentre + y, ycentre - x, opacity, red, green, blue);
	  plot_blend( xcentre - y, ycentre - x, opacity, red, green, blue);
	}
  */
}
//

void pngwriter::circle_blend(const unsigned & xcentre,
			     const unsigned & ycentre, const unsigned & radius,
			     const double & opacity, const int & red,
			     const int & green, const int & blue)
{
  master_circle( xcentre, ycentre, radius, red, green, blue, opacity );
  /*
  int x = 0;
  int y = radius;
  int p = (5 - radius*4)/4;

  circle_aux_blend(xcentre, ycentre, x, y, opacity, red, green, blue);
  while (x < y)
    {
      x++;
      if (p < 0)
	{
	  p += 2*x+1;
	}
      else
	{
	  y--;
	  p += 2*(x-y)+1;
	}
      circle_aux_blend(xcentre, ycentre, x, y, opacity, red, green, blue);
    }
  */
}

void pngwriter::circle_blend(const unsigned & xcentre,
			     const unsigned & ycentre, const unsigned & radius,
			     const double & opacity, const pngwriter::Byte & red,
			     const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_circle( xcentre, ycentre, radius, int( red ) << 8, int( green ) << 8,
		 int( blue ) << 8, opacity );
}

void pngwriter::circle_blend(const unsigned & xcentre,
			     const unsigned & ycentre, const unsigned & radius,
			     const double & opacity, const double & red,
			     const double & green, const double & blue)
{
  master_circle( xcentre, ycentre, radius, int( red * 65535. ),
		 int( green * 65535. ), int( blue * 65535. ), opacity );
}

void pngwriter::filledcircle_blend(const unsigned & xcentre,
				   const unsigned & ycentre,
				   const unsigned & radius,
				   const double & opacity,
				   const int & red, const int & green,
				   const int & blue)
{
  master_filledcircle( xcentre, ycentre, radius, red, green, blue, opacity );
}

void pngwriter::filledcircle_blend(const unsigned & xcentre,
				   const unsigned & ycentre,
				   const unsigned & radius,
				   const double & opacity,
				   const pngwriter::Byte & red, const pngwriter::Byte & green,
				   const pngwriter::Byte & blue)
{
  master_filledcircle( xcentre, ycentre, radius,
		       int( red ) << 8,
		       int( green ) << 8,
		       int( blue ) << 8, opacity );
}

void pngwriter::filledcircle_blend(const unsigned & xcentre,
				   const unsigned & ycentre,
				   const unsigned & radius,
				   const double & opacity,
				   const double & red, const double & green,
				   const double & blue)
{
  master_filledcircle( xcentre, ycentre,  radius, int( red * 65535 ),
		       int( green * 65535 ), int( blue * 65535 ), opacity);
}

void pngwriter::bezier_blend( const int & startPtX, const int & startPtY,
			      const int & startControlX,
			      const int & startControlY,
			      const int & endPtX, const int & endPtY,
			      const int & endControlX, const int & endControlY,
			      const double & opacity, const double & red,
			      const double & green, const double & blue )
{
  master_bezier( startPtX, startPtY, startControlX, startControlY,
		 endPtX, endPtY, endControlX, endControlY,
		 int( red * 65535. ), int( green* 65535. ),
		 int( blue* 65535. ), opacity );
  /*
  double cx = 3.0*(startControlX - startPtX);
  double bx = 3.0*(endControlX - startControlX) - cx;
  double ax = double(endPtX - startPtX - cx - bx);

  double cy = 3.0*(startControlY - startPtY);
  double by = 3.0*(endControlY - startControlY) - cy;
  double ay = double(endPtY - startPtY - cy - by);

  double x,y,newx,newy;
  x = startPtX;
  y = startPtY;

  for(double t = 0.0; t<=1.005; t += 0.005)
    {
      newx = startPtX + t*(double(cx) + t*(double(bx) + t*(double(ax))));
      newy = startPtY + t*(double(cy) + t*(double(by) + t*(double(ay))));
      line_blend(int(x),int(y),int(newx),int(newy),opacity, red,green,blue);
      x = newx;
      y = newy;
    }
  */
}

void pngwriter::bezier_blend( const int & startPtX, const int & startPtY,
			      const int & startControlX,
			      const int & startControlY,
			      const int & endPtX, const int & endPtY,
			      const int & endControlX, const int & endControlY,
			      const double & opacity, const pngwriter::Byte & red,
			      const pngwriter::Byte & green, const pngwriter::Byte & blue )
{
  master_bezier( startPtX, startPtY, startControlX, startControlY,
		 endPtX, endPtY, endControlX,  endControlY,
		 int( red ) << 8, int( green ) << 8, int( blue ) << 8,
		 opacity );

}

void pngwriter::bezier_blend( const int & startPtX, const int & startPtY,
			      const int & startControlX,
			      const int & startControlY,
			      const int & endPtX, const int & endPtY,
			      const int & endControlX, const int & endControlY,
			      const double & opacity, const int & red,
			      const int & green, const int & blue )
{
  master_bezier( startPtX, startPtY, startControlX, startControlY,
		 endPtX, endPtY, endControlX,  endControlY,
		 red, green, blue, opacity );

}

/////////////////////////////
#ifndef NO_FREETYPE

// Freetype-based text rendering functions.
///////////////////////////////////////////
void pngwriter::plot_text_blend( char * face_path, const int & fontsize,
				 const unsigned & x_start,
				 const unsigned & y_start,
				 const double & angle, char * text,
				 const double & opacity, const double & red,
				 const double & green, const double & blue)
{
  master_plot_text( face_path, fontsize, x_start, y_start, angle, text,
		    red, green, blue, opacity );
  // FT_Library  library;
  // FT_Face     face;
  // FT_Matrix   matrix;      // transformation matrix
  // FT_Vector   pen;

  // FT_UInt glyph_index;
  // FT_Error error;

  // FT_Bool use_kerning;
  // FT_UInt previous = 0;

  // /* Set up transformation Matrix */
  // matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
  // matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
  // matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
  // matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;

  // /* Place starting coordinates in adequate form. */
  // pen.x = x_start*64 ;
  // pen.y =   (int)(y_start/64.0);

  // /*Count the length of the string */
  // int num_chars = strlen(text);

  // /* Initialize FT Library object */
  // error = FT_Init_FreeType( &library );
  // if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Could not init Library."<< std::endl; return;}

  // /* Initialize FT face object */
  // error = FT_New_Face( library,face_path,0,&face );
  // if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; }

  // /* Set the Char size */
  // error = FT_Set_Char_Size( face,          /* handle to face object           */
  // 			    0,             /* char_width in 1/64th of points  */
  // 			    fontsize*64,   /* char_height in 1/64th of points */
  // 			    100,           /* horizontal device resolution    */
  // 			    100 );         /* vertical device resolution      */

  // /* A way of accesing the glyph directly */
  // FT_GlyphSlot  slot = face->glyph;  // a small shortcut

  // /* Does the font file support kerning? */
  // use_kerning = FT_HAS_KERNING( face );

  // int n;
  // for ( n = 0; n < num_chars; n++ )
  //   {
  //     /* Convert character code to glyph index */
  //     glyph_index = FT_Get_Char_Index( face, text[n] );

  //     /* Retrieve kerning distance and move pen position */
  //     if ( use_kerning && previous&& glyph_index )
  // 	{
  // 	  FT_Vector  delta;
  // 	  FT_Get_Kerning( face,
  // 			  previous,
  // 			  glyph_index,
  // 			  ft_kerning_default, //FT_KERNING_DEFAULT,
  // 			  &delta );

  // 	  /* Transform this kerning distance into rotated space */
  // 	  pen.x += (int) (((double) delta.x)*cos(angle));
  // 	  pen.y +=  (int) (((double) delta.x)*( sin(angle)));
  // 	}

  //     /* Set transform */
  //     FT_Set_Transform( face, &matrix, &pen );

  //     /*set char size*/

  //     if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Set char size error." << std::endl; return;}

  //     /* Retrieve glyph index from character code */
  //     glyph_index = FT_Get_Char_Index( face, text[n] );

  //     /* Load glyph image into the slot (erase previous one) */
  //     error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
  //     if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}

  //     /* Convert to an anti-aliased bitmap */
  //     //	error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
  //     error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
  //     if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Render glyph error." << std::endl; return;}

  //     /* Now, draw to our target surface */
  //     my_draw_bitmap_blend( &slot->bitmap,
  // 			    slot->bitmap_left,
  // 			    y_start + slot->bitmap_top,
  // 			    opacity,
  // 			    red,
  // 			    green,
  // 			    blue );

  //     /* Advance to the next position */
  //     pen.x += slot->advance.x;
  //     pen.y += slot->advance.y;

  //     /* record current glyph index */
  //     previous = glyph_index;
  //   }

  // /* Free the face and the library objects */
  // FT_Done_Face    ( face );
  // FT_Done_FreeType( library );
}

void pngwriter::plot_text_utf8_blend( char * face_path, const int & fontsize,
				      const unsigned & x_start,
				      const unsigned & y_start,
				      const double & angle,  char * text,
				      const double & opacity,
				      const double & red,
				      const double & green, const double & blue)
{
  master_plot_text_utf8( face_path, fontsize, x_start, y_start, angle, text,
			 red, green, blue, opacity );

  // FT_Library  library;
  // FT_Face     face;
  // FT_Matrix   matrix;      // transformation matrix
  // FT_Vector   pen;

  // FT_UInt glyph_index;
  // FT_Error error;

  // FT_Bool use_kerning;
  // FT_UInt previous = 0;

  // /* Set up transformation Matrix */
  // matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
  // matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
  // matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
  // matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;

  // /* Place starting coordinates in adequate form. */
  // pen.x = x_start*64 ;
  // pen.y = (int)(y_start/64.0);

  // /*Count the length of the string */
  // int num_bytes=0;
  // while(text[num_bytes]!=0)
  //   {
  //     num_bytes++;
  //   }

  // /*
  //   std::cout << "Num bytes is: "<< num_bytes << std::endl;
  // */

  // //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file.
  // long * ucs4text;
  // ucs4text = new long[num_bytes+1];

  // Byte u,v,w,x,y,z;

  // int num_chars=0;

  // long iii=0;

  // while(iii<num_bytes)
  //   {
  //     z = text[iii];

  //     if(z<=127)
  // 	{
  // 	  ucs4text[num_chars] = z;
  // 	}

  //     if((192<=z)&&(z<=223))
  // 	{
  // 	  iii++; y = text[iii];
  // 	  ucs4text[num_chars] = (z-192)*64 + (y -128);
  // 	}

  //     if((224<=z)&&(z<=239))
  // 	{
  // 	  iii++; y = text[iii];
  // 	  iii++; x = text[iii];
  // 	  ucs4text[num_chars] = (z-224)*4096 + (y -128)*64 + (x-128);
  // 	}

  //     if((240<=z)&&(z<=247))
  // 	{
  // 	  iii++; y = text[iii];
  // 	  iii++; x = text[iii];
  // 	  iii++; w = text[iii];
  // 	  ucs4text[num_chars] = (z-240)*262144 + (y -128)*4096 + (x-128)*64 + (w-128);
  // 	}

  //     if((248<=z)&&(z<=251))
  // 	{
  // 	  iii++; y = text[iii];
  // 	  iii++; x = text[iii];
  // 	  iii++; w = text[iii];
  // 	  iii++; v = text[iii];
  // 	  ucs4text[num_chars] = (z-248)*16777216 + (y -128)*262144 + (x-128)*4096 + (w-128)*64 +(v-128);
  // 	}

  //     if((252==z)||(z==253))
  // 	{
  // 	  iii++; y = text[iii];
  // 	  iii++; x = text[iii];
  // 	  iii++; w = text[iii];
  // 	  iii++; v = text[iii];
  // 	  u = text[iii];
  // 	  ucs4text[num_chars] = (z-252)*1073741824 + (y -128)*16777216   + (x-128)*262144 + (w-128)*4096 +(v-128)*64 + (u-128);
  // 	}

  //     if((z==254)||(z==255))
  // 	{
  // 	  std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: Problem with character: invalid UTF-8 data."<< std::endl;
  // 	}
  //     // std::cerr << "\nProblem at " << iii << ".\n";
  //     //
  //     iii++;
  //     num_chars++;
  //   }

  // // num_chars now contains the number of characters in the string.
  // /*
  //   std::cout << "Num chars is: "<< num_chars << std::endl;
  // */

  // /* Initialize FT Library object */
  // error = FT_Init_FreeType( &library );
  // if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Could not init Library."<< std::endl; return;}

  // /* Initialize FT face object */
  // error = FT_New_Face( library,face_path,0,&face );
  // if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; }

  // /* Set the Char size */
  // error = FT_Set_Char_Size( face,          /* handle to face object           */
  // 			    0,             /* char_width in 1/64th of points  */
  // 			    fontsize*64,   /* char_height in 1/64th of points */
  // 			    100,           /* horizontal device resolution    */
  // 			    100 );         /* vertical device resolution      */

  // /* A way of accesing the glyph directly */
  // FT_GlyphSlot  slot = face->glyph;  // a small shortcut

  // /* Does the font file support kerning? */
  // use_kerning = FT_HAS_KERNING( face );

  // int n;
  // for ( n = 0; n < num_chars; n++ )
  //   {
  //     /* Convert character code to glyph index */
  //     glyph_index = FT_Get_Char_Index( face, ucs4text[n] );

  //     /* Retrieve kerning distance and move pen position */
  //     if ( use_kerning && previous&& glyph_index )
  // 	{
  // 	  FT_Vector  delta;
  // 	  FT_Get_Kerning( face,
  // 			  previous,
  // 			  glyph_index,
  // 			  ft_kerning_default, //FT_KERNING_DEFAULT,
  // 			  &delta );

  // 	  /* Transform this kerning distance into rotated space */
  // 	  pen.x += (int) (((double) delta.x)*cos(angle));
  // 	  pen.y +=  (int) (((double) delta.x)*( sin(angle)));
  // 	}

  //     /* Set transform */
  //     FT_Set_Transform( face, &matrix, &pen );

  //     /*set char size*/

  //     if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Set char size error." << std::endl; return;}

  //     /* Retrieve glyph index from character code */
  //     glyph_index = FT_Get_Char_Index( face, ucs4text[n] );

  //     /* Load glyph image into the slot (erase previous one) */
  //     error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
  //     if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}

  //     /* Convert to an anti-aliased bitmap */
  //     error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
  //     if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Render glyph error." << std::endl; return;}

  //     /* Now, draw to our target surface */
  //     my_draw_bitmap_blend( &slot->bitmap,
  // 			    slot->bitmap_left,
  // 			    y_start + slot->bitmap_top,
  // 			    opacity,
  // 			    red,
  // 			    green,
  // 			    blue );

  //     /* Advance to the next position */
  //     pen.x += slot->advance.x;
  //     pen.y += slot->advance.y;

  //     /* record current glyph index */
  //     previous = glyph_index;
  //   }

  // /* Free the face and the library objects */
  // FT_Done_Face    ( face );
  // FT_Done_FreeType( library );

  // delete[] ucs4text;
}

void pngwriter::plot_text_blend( char * face_path, const int & fontsize,
				 const unsigned & x_start,
				 const unsigned & y_start,
				 const double & angle, char * text,
				 const double & opacity, const pngwriter::Byte & red,
				 const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_plot_text( face_path, fontsize, x_start, y_start,  angle,  text,
		    int( red ) << 8, int( green ) << 8,
		    int( blue ) << 8, opacity );
}

void pngwriter::plot_text_blend( char * face_path, const int & fontsize,
				 const unsigned & x_start,
				 const unsigned & y_start,
				 const double & angle, char * text,
				 const double & opacity, const int & red,
				 const int & green, const int & blue)
{
  master_plot_text( face_path, fontsize, x_start, y_start, angle, text,
		    int( red * 65535. ),  int( green * 65535. ),
		    int( blue * 65535. ), opacity );
}

void pngwriter::plot_text_utf8_blend( char * face_path, const int & fontsize,
				      const unsigned & x_start,
				      const unsigned & y_start,
				      const double & angle, char * text,
				      const double & opacity, const pngwriter::Byte & red,
				      const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_plot_text_utf8( face_path, fontsize, x_start, y_start,  angle, text,
			 int( red ) << 8, int( green ) << 8,
			int( blue ) << 8, opacity );
}

void pngwriter::plot_text_utf8_blend( char * face_path, const int & fontsize,
				      const unsigned & x_start,
				      const unsigned & y_start,
				      const double & angle, char * text,
				      const double & opacity, const int & red,
				      const int & green, const int & blue)
{
  master_plot_text_utf8( face_path, fontsize, x_start, y_start, angle, text,
			 int( red * 65535. ),  int( green * 65535. ),
			 int( blue * 65535. ), opacity );
}

void pngwriter::my_draw_bitmap_blend(FT_Bitmap * bitmap,
				     const unsigned & x,
				     const unsigned & y,
				     const double & opacity,
				     const double & red, const double & green,
				     const double & blue)
{
  master_my_draw_bitmap( bitmap, x, y, red, green, blue, opacity );
  /*  
  double temp;
  for(int j=1; j<bitmap->rows+1; j++)
    {
      for(int i=1; i< bitmap->width + 1; i++)
	{
	  temp = (double)(bitmap->buffer[(j-1)*bitmap->width + (i-1)] )/255.0;

	  if(temp)
	    {
	      plot_blend(x + i,
			 y  - j,
			 opacity,
			 temp*red + (1-temp)*(dread(x+i,y-j, tRed)),
			 temp*green + (1-temp)*(dread(x+i,y-j,tGreen)),
			 temp*blue + (1-temp)*(dread(x+i,y-j, tBlue))
			 );
	    }
	}
    }
  */
}

#endif
#ifdef NO_FREETYPE

void pngwriter::plot_text_blend( char * face_path, const int & fontsize,
				 const unsigned & x_start,
				 const unsigned & y_start,
				 const double & angle, char * text,
				 const double & opacity, const pngwriter::Byte & red,
				 const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  std::cerr << " PNGwriter::plot_text_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

void pngwriter::plot_text_blend( char * face_path, const int & fontsize,
				 const unsigned & x_start,
				 const unsigned & y_start,
				 const double & angle, char * text,
				 const double & opacity, const double & red,
				 const double & green, const double & blue)
{
  std::cerr << " PNGwriter::plot_text_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;

}

void pngwriter::plot_text_utf8_blend( char * face_path, const int & fontsize,
				      const unsigned & x_start,
				      const unsigned & y_start,
				      const double & angle, char * text,
				      const double & opacity, const int & red,
				      const int & green, const int & blue)
{
  std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

void pngwriter::plot_text_utf8_blend( char * face_path, const int & fontsize,
				      const unsigned & x_start,
				      const unsigned & y_start,
				      const double & angle, char * text,
				      const double & opacity, const pngwriter::Byte & red,
				      const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

void pngwriter::plot_text_utf8_blend( char * face_path, const int & fontsize,
				      const unsigned & x_start,
				      const unsigned & y_start,
				      const double & angle, char * text,
				      const double & opacity,
				      const double & red,
				      const double & green, const double & blue)
{
  std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
  return;
}

#endif

///////////////////////////

void pngwriter::boundary_fill_blend(const unsigned & xstart,
				    const unsigned & ystart,
				    const double & opacity,
				    const double & boundary_red,
				    const double & boundary_green,
				    const double & boundary_blue,
				    const double & fill_red,
				    const double & fill_green,
				    const double & fill_blue)
{
  master_boundary_fill( xstart, ystart,
			boundary_red, boundary_green, boundary_blue,
			fill_red, fill_green, fill_blue, opacity );
  /*
  if( (
       (dread(xstart,ystart, tRed ) != boundary_red) ||
       (dread(xstart,ystart, tGreen ) != boundary_green) ||
       (dread(xstart,ystart, tBlue ) != boundary_blue)
       )
      &&
      (
       (dread(xstart,ystart, tRed ) != fill_red) ||
       (dread(xstart,ystart, tGreen ) != fill_green) ||
       (dread(xstart,ystart, tBlue ) != fill_blue)
       )
      &&
      (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_)
      )
    {
      plot_blend(xstart, ystart, opacity,  fill_red, fill_green, fill_blue);
      boundary_fill_blend(xstart+1,  ystart, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
      boundary_fill_blend(xstart,  ystart+1, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
      boundary_fill_blend(xstart,  ystart-1, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
      boundary_fill_blend(xstart-1,  ystart, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
    }
  */
}

//no int version needed
void pngwriter::flood_fill_internal_blend(const unsigned & xstart,
					  const unsigned & ystart,
					  const double & opacity, 
					  const double & start_red,
					  const double & start_green,
					  const double & start_blue,
					  const double & fill_red,
					  const double & fill_green,
					  const double & fill_blue)
{
  master_flood_fill_internal( xstart, ystart,
			      start_red, start_green, start_blue,
			      fill_red, fill_green, fill_blue, opacity );
  /*
  if( (
       (dread(xstart,ystart, tRed ) == start_red) &&
       (dread(xstart,ystart, tGreen ) == start_green) &&
       (dread(xstart,ystart, tBlue ) == start_blue)
       )
      &&
      (
       (dread(xstart,ystart, tRed ) != fill_red) ||
       (dread(xstart,ystart, tGreen ) != fill_green) ||
       (dread(xstart,ystart, tBlue ) != fill_blue)
       )
      &&
      (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_)
      )
    {
      plot_blend(xstart, ystart, opacity, fill_red, fill_green, fill_blue);
      flood_fill_internal_blend(  xstart+1,  ystart, opacity,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
      flood_fill_internal_blend(  xstart-1,  ystart,opacity,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
      flood_fill_internal_blend(  xstart,  ystart+1, opacity,  start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
      flood_fill_internal_blend(  xstart,  ystart-1, opacity,  start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
    }
  */
}

//Byte version
void pngwriter::boundary_fill_blend(const unsigned & xstart,
				    const unsigned & ystart,
				    const double & opacity,
				    const pngwriter::Byte & boundary_red,
				    const pngwriter::Byte & boundary_green,
				    const pngwriter::Byte & boundary_blue,
				    const pngwriter::Byte & fill_red,
				    const pngwriter::Byte & fill_green,
				    const pngwriter::Byte & fill_blue)
{

  boundary_fill_blend( xstart, ystart,
		       opacity,
		       double( boundary_red ) / 255.,
		       double( boundary_green ) / 255.,
		       double( boundary_blue ) / 255.,
		       double( fill_red ) / 255.,
		       double( fill_green ) / 255.,
		       double( fill_blue ) / 255.
		       );
}

//int version
void pngwriter::boundary_fill_blend(const unsigned & xstart,
				    const unsigned & ystart,
				    const double & opacity,
				    const int & boundary_red,
				    const int & boundary_green,
				    const int & boundary_blue,
				    const int & fill_red,
				    const int & fill_green,
				    const int & fill_blue)
{

  boundary_fill_blend( xstart, ystart,
		       opacity,
		       ((double) boundary_red)/65535.0,
		       ((double) boundary_green)/65535.0,
		       ((double) boundary_blue)/65535.0,
		       ((double) fill_red)/65535.0,
		       ((double) fill_green)/65535.0,
		       ((double) fill_blue)/65535.0
		       );
}

void pngwriter::flood_fill_blend(const unsigned & xstart,
				 const unsigned & ystart,
				 const double & opacity,
				 const double & fill_red,
				 const double & fill_green,
				 const double & fill_blue)
{
  master_flood_fill(  xstart,  ystart,
		      int( fill_red * 65535. ),
		      int( fill_green * 65535. ),
		      int(  fill_blue * 65535. ), opacity );
}

//Byte version
void pngwriter::flood_fill_blend(const unsigned & xstart,
				 const unsigned & ystart,
				 const double & opacity,
				 const pngwriter::Byte & fill_red,
				 const pngwriter::Byte & fill_green,
				 const pngwriter::Byte & fill_blue)
{
  master_flood_fill( xstart,  ystart,
		    int( fill_red ) << 8, int( fill_green ) << 8,
		    int( fill_blue ) << 8, opacity );
}

//int version
void pngwriter::flood_fill_blend(const unsigned & xstart,
				 const unsigned & ystart,
				 const double & opacity,
				 const int & fill_red,
				 const int & fill_green,
				 const int & fill_blue)
{
  master_flood_fill( xstart,  ystart,
		     fill_red, fill_green, fill_blue, opacity );
}

void pngwriter::polygon_blend( unsigned * points,
			       const unsigned & number_of_points,
			       const double & opacity, const double & red,
			       const double & green, const double & blue)
{
  master_polygon( points, number_of_points, int( red * 65535. ),
		  int( green * 65535. ), int( blue * 65535. ), opacity );
}

//Byte version
void pngwriter::polygon_blend( unsigned * points,
			       const unsigned & number_of_points,
			       const double & opacity, const pngwriter::Byte & red,
			       const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_polygon( points, number_of_points, int( red ) << 8, int( green ) << 8,
		  int( blue ) << 8, opacity );
}

//int version
void pngwriter::polygon_blend( unsigned * points,
			       const unsigned & number_of_points,
			       const double & opacity, const int & red,
			       const int & green, const int & blue)
{
  master_polygon( points, number_of_points, red, green, blue, opacity );
}

void pngwriter::plotCMYK_blend(const unsigned & x, const unsigned & y,
			       const double & opacity, const double & cyan,
			       const double & magenta, const double & yellow,
			       const double & black)
{
  master_plotCMYK( x, y, cyan, magenta, yellow, black, opacity );
  // /*CMYK to RGB:
  //  *  -----------
  //  *  red   = 255 - minimum(255,((cyan/255)    * (255 - black) + black))
  //  *  green = 255 - minimum(255,((magenta/255) * (255 - black) + black))
  //  *  blue  = 255 - minimum(255,((yellow/255)  * (255 - black) + black))
  //  * */

  // if(cyan<0.0)
  //   {
  //     cyan = 0.0;
  //   }
  // if(magenta<0.0)
  //   {
  //     magenta = 0.0;
  //   }
  // if(yellow<0.0)
  //   {
  //     yellow = 0.0;
  //   }
  // if(black<0.0)
  //   {
  //     black = 0.0;
  //   }

  // if(cyan>1.0)
  //   {
  //     cyan = 1.0;
  //   }
  // if(magenta>1.0)
  //   {
  //     magenta = 1.0;
  //   }
  // if(yellow>1.0)
  //   {
  //     yellow = 1.0;
  //   }
  // if(black>1.0)
  //   {
  //     black = 1.0;
  //   }

  // double  red, green, blue, minr, ming, minb, iblack;

  // iblack = 1.0 - black;

  // minr = 1.0;
  // ming = 1.0;
  // minb = 1.0;

  // if( (cyan*iblack + black)<1.0 )
  //   {
  //     minr = cyan*iblack + black;
  //   }

  // if( (magenta*iblack + black)<1.0 )
  //   {
  //     ming = magenta*iblack + black;
  //   }

  // if( (yellow*iblack + black)<1.0 )
  //   {
  //     minb = yellow*iblack + black;
  //   }

  // red = 1.0 - minr;
  // green = 1.0 - ming;
  // blue = 1.0 - minb;

  // plot_blend(x,y,opacity, red, green, blue);
}

//Byte version
void pngwriter::plotCMYK_blend(const unsigned & x, const unsigned & y,
			       const double & opacity, const pngwriter::Byte & cyan,
			       const pngwriter::Byte & magenta, const pngwriter::Byte & yellow,
			       const pngwriter::Byte & black)
{
  master_plotCMYK( x, y,
		   double( cyan ) / 255.,
		   double( magenta ) / 255.,
		   double( yellow ) / 255.,
		   double( black ) / 255.,
		   opacity );
}

//int version
void pngwriter::plotCMYK_blend(const unsigned & x, const unsigned & y,
			       const double & opacity, const int & cyan,
			       const int & magenta, const int & yellow,
			       const int & black)
{
  master_plotCMYK( x, y,
		   double( cyan ) / 65535.,
		   double( magenta ) / 65535.,
		   double( yellow ) / 65535.,
		   double( black ) / 65535.,
		   opacity
		   );
}

void pngwriter::laplacian(const double & k, const double & offset)
{

  // Create image storage.
  pngwriter temp(width_,height_,0,"temp");

  double red, green, blue;

  unsigned x, y;

  for(x = 1; x <= width_; x++)
    {
      for(y = 1; y <= height_; y++)
	{
	  red = 8.0*dread(x,y, tRed ) -
	    ( dread(x+1, y-1, tRed ) +
	      dread(x,   y-1, tRed ) +
	      dread(x-1, y-1, tRed ) +
	      dread(x-1, y  , tRed ) +
	      dread(x+1, y  , tRed ) +
	      dread(x+1, y+1, tRed ) +
	      dread(x,   y+1, tRed ) +
	      dread(x-1, y+1, tRed ) );

	  green = 8.0*dread(x,y, tGreen ) -
	    ( dread(x+1, y-1, tGreen ) +
	      dread(x,   y-1, tGreen ) +
	      dread(x-1, y-1, tGreen ) +
	      dread(x-1, y  , tGreen ) +
	      dread(x+1, y  , tGreen ) +
	      dread(x+1, y+1, tGreen ) +
	      dread(x,   y+1, tGreen ) +
	      dread(x-1, y+1, tGreen ));

	  blue = 8.0*dread(x,y, tBlue ) -
	    ( dread(x+1, y-1, tBlue ) +
	      dread(x,   y-1, tBlue ) +
	      dread(x-1, y-1, tBlue ) +
	      dread(x-1, y  , tBlue ) +
	      dread(x+1, y  , tBlue ) +
	      dread(x+1, y+1, tBlue ) +
	      dread(x,   y+1, tBlue ) +
	      dread(x-1, y+1, tBlue ));

	  temp.plot(x,y,offset+k*red,offset+k*green,offset+k*blue);

	}
    }

  *this = temp;
  // for( unsigned xx = 1; xx <= width_; xx++)
  //   {
  //     for( unsigned yy = 1; yy <= height_; yy++)
  // 	{
  // 	  plot( xx, yy, temp.read(xx,yy, tRed ),
  // 		temp.read(xx,yy, tGreen ), temp.read(xx,yy, tBlue ) );
  // 	}
  //   }
}



// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
// ( <gurkan@linuks.mine.nu>, http://www.linuks.mine.nu/ )
void pngwriter::master_drawtop(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3,
			       const int & red, const int & green,
			       const int & blue, const double & opacity)
{
  // This swaps x2 and x3
  // if(x2>x3) x2^=x3^=x2^=x3;
  unsigned locX2( std::min( x2, x3 ) ),
    locX3( std::max( x2, x3 ) );

  // std::cout << "(x2, x3) = (" << x2 << ", " << x3 << ") => loc = ("
  // 	    << locX2 << ", " << locX3 << ")" << std::endl;

  long posl( x1 * 256 );
  long posr( posl );

  bool same;

  long cl, cr;

  cl = difference( long( locX2 ), long( x1 ), same );
  if ( ! same ) cl *= -1.;
  cl *= 256;
  cl /= difference( long( y2 ), long( y1 ), same );
  if ( ! same ) cl *= -1.;

  cr = difference( long( locX3 ), long( x1 ), same );
  if ( ! same ) cr *= -1.;
  cr *= 256;
  cr /= difference( long( y2 ), long( y1 ), same );
  if ( ! same ) cr *= -1.;

  for(unsigned y( y1 ); y < y2; y++)
    {
       // std::cout << "line from (" << unsigned(posl / 256) << ", " << y
       // 		<< ") to ("
       // 		<< unsigned(posr / 256) << ", " << y << ")" << std::endl;
      master_line( unsigned( posl / 256 ), y, unsigned( posr / 256 ), y,
		   red, green, blue, opacity );
      posl += cl;
      posr += cr;
    }
}

void pngwriter::drawtop(const unsigned & x1, const unsigned & y1,
			const unsigned & x2, const unsigned & y2,
			const unsigned & x3,
			const int & red, const int & green, const int & blue)
{
  master_drawtop( x1, y1, x2, y2, x3, red, green, blue );
}

// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
// ( <gurkan@linuks.mine.nu>, http://www.linuks.mine.nu/ )
void pngwriter::master_drawbottom(const unsigned & x1, const unsigned & y1,
				  const unsigned & x2, const unsigned & x3,
				  const unsigned & y3,
				  const int & red, const int & green,
				  const int & blue, const double & opacity)
{
  //Swap x1 and x2
  //if(x1>x2) x2^=x1^=x2^=x1;
  unsigned locX1( std::min( x1, x2 ) ),
    locX2( std::max( x1, x2 ) );

  long posl( locX1 * 256 );
  long posr( locX2 * 256 );

  bool same;

  long  cl, cr;
  
  cl = difference( long( x3 ), long( locX1 ), same );
  if ( ! same ) cl *= -1.;
  cl *= 256;
  cl /= difference( long( y3 ), long( y1 ), same );
  if ( ! same ) cl *= -1.;

  cr = difference( long( x3 ), long( locX2 ), same );
  if ( ! same ) cr *= -1.;
  cr *= 256;
  cr /= difference( long( y3 ), long( y1 ), same );
  if ( ! same ) cr *= -1.;

  for(unsigned y( y1 ); y<y3; y++)
    {
      // std::cout << "line from (" << unsigned(posl / 256) << ", " << y
      // 		<< ") to ("
      // 		<< unsigned(posr / 256) << ", " << y << ")" << std::endl;
      master_line( unsigned( posl / 256 ), y, unsigned( posr / 256 ), y,
		   red, green, blue, opacity );
      posl+=cl;
      posr+=cr;
    }
}

void pngwriter::drawbottom(const unsigned & x1, const unsigned & y1,
			   const unsigned & x2, const unsigned & x3,
			   const unsigned & y3,
			   const int & red, const int & green, const int & blue)
{
  master_drawbottom( x1, y1, x2, x3, y3, red, green, blue );
}
// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
// ( <gurkan@linuks.mine.nu>, http://www.linuks.mine.nu/ )
void pngwriter::master_filledtriangle(const unsigned & x1, const unsigned & y1,
				      const unsigned & x2, const unsigned & y2,
				      const unsigned & x3, const unsigned & y3,
				      const int & red, const int & green,
				      const int & blue, const double & opacity)
{
  if( ( x1==x2 && x2==x3 ) ||
      ( y1==y2 && y2==y3 ) ) return;

  std::multimap< unsigned, unsigned > sorter;

  sorter.insert( std::make_pair( y1, x1 ) );
  sorter.insert( std::make_pair( y2, x2 ) );
  sorter.insert( std::make_pair( y3, x3 ) );

  unsigned locX1( sorter.begin()->second ), locX2( (++sorter.begin())->second ),
    locX3( sorter.rbegin()->second ), locY1( sorter.begin()->first ),
    locY2( (++sorter.begin())->first ), locY3( sorter.rbegin()->first );

  if ( locY2 == locY3 )
    {
      master_drawtop( locX1, locY1, locX2, locY2, locX3, red, green, blue,
		     opacity );
    }
  else
    {
      if ( locY1==locY3 || locY1==locY2 )
	{
	  master_drawbottom( locX1, locY1, locX2, locX3, locY3,
			     red, green, blue, opacity );
	}
      else
	{
	  bool same;
	  long frac( difference( long( locY2 ), long(  locY1 ), same ) * 256 );
	  if ( ! same ) frac *= -1;
	  frac *= difference( long( locX3 ), long( locX1 ), same );
	  if ( ! same ) frac *= -1;
	  frac /= difference( long( locY3 ), long( locY1 ), same );
	  if ( ! same ) frac *= -1;
	  frac += long( locX1 ) * 256;
	  unsigned new_x( frac >> 8 );

	  master_drawtop( locX1, locY1, new_x, locY2, locX2,
			  red, green, blue, opacity );
	  master_drawbottom( locX2, locY2, new_x, locX3, locY3,
			     red, green, blue, opacity ); 
	}
    }

}

void pngwriter::filledtriangle(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3, const unsigned & y3,
			       const int & red, const int & green,
			       const int & blue)
{
  master_filledtriangle( x1, y1, x2, y2, x3, y3, red, green, blue );
}

//Byte
void pngwriter::filledtriangle(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3, const unsigned & y3,
			       const pngwriter::Byte & red, const pngwriter::Byte & green,
			       const pngwriter::Byte & blue)
{
  master_filledtriangle(x1, y1, x2, y2, x3, y3,
			int( red ) << 8,
			int( green ) << 8,
			int( blue ) << 8 ); 
}

//Double (bug found by Dave Wilks. Was: (int) red*65535, should have been (int) (red*65535).
void pngwriter::filledtriangle(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3, const unsigned & y3,
			       const double & red, const double & green,
			       const double & blue)
{
  master_filledtriangle( x1, y1, x2, y2, x3, y3, int( red * 65535. ),
			 int( green * 65535. ),  int( blue * 65535 ) ); 
}

//Blend, double. (bug found by Dave Wilks. Was: (int) red*65535, should have been (int) (red*65535).
void pngwriter::filledtriangle_blend(const unsigned & x1, const unsigned & y1,
				     const unsigned & x2, const unsigned & y2,
				     const unsigned & x3, const unsigned & y3,
				     const double & opacity,
				     const double & red, const double & green,
				     const double & blue)
{
  master_filledtriangle( x1, y1, x2, y2, x3, y3,
			 int( red * 65535. ),
			 int( green * 65535. ),
			 int( blue * 65535. ), opacity );
}

void pngwriter::filledtriangle_blend(const unsigned & x1, const unsigned & y1,
				     const unsigned & x2, const unsigned & y2,
				     const unsigned & x3, const unsigned & y3,
				     const double & opacity,
				     const pngwriter::Byte & red, const pngwriter::Byte & green,
				     const pngwriter::Byte & blue)
{
  master_filledtriangle( x1, y1, x2, y2, x3, y3,
			 int( red ) << 8,
			 int( green ) << 8,
			 int( blue ) << 8, opacity );
}

//Blend, int
void pngwriter::filledtriangle_blend(const unsigned & x1, const unsigned & y1,
				     const unsigned & x2, const unsigned & y2,
				     const unsigned & x3, const unsigned & y3,
				     const double & opacity,
				     const int & red, const int & green,
				     const int & blue)
{
  master_filledtriangle( x1, y1, x2, y2, x3, y3, red, green, blue, opacity );
  /*
  if((x1==x2 && x2==x3) || (y1==y2 && y2==y3)) return;

  std::multimap< unsigned, unsigned > sorter;

  sorter.insert( std::make_pair( y1, x1 ) );
  sorter.insert( std::make_pair( y2, x2 ) );
  sorter.insert( std::make_pair( y3, x3 ) );

  unsigned locX1( sorter.begin()->second ), locX2( (++sorter.begin())->second ),
    locX3( sorter.rbegin()->second ), locY1( sorter.begin()->first ),
    locY2( (++sorter.begin())->first ), locY3( sorter.rbegin()->first );

  if(locY2==locY3)
    {
      drawtop_blend(locX1, locY1, locX2, locY2, locX3, opacity, red, green, blue);
    }
  else
    {
      if(locY1==locY3 || locY1==locY2)
	{
	  drawbottom_blend(locX1, locY1, locX2, locX3, locY3, opacity, red, green, blue);
	}
      else
	{
	  unsigned new_x( locX1 );
	  new_x += unsigned( double( ( locY2 -locY1 ) * ( locX3 - locX1 ) ) /
			     double( locY3 -locY1 ) );
	  drawtop_blend(locX1, locY1, new_x, locY2, locX2, opacity,  red, green, blue);
	  drawbottom_blend(locX2, locY2, new_x, locX3, locY3, opacity, red, green, blue);
	}
    }
  */
}

//Blend, int
void pngwriter::drawbottom_blend(const unsigned & x1, const unsigned & y1,
				 const unsigned & x2, const unsigned & x3,
				 const unsigned & y3, const double & opacity,
				 const int & red, const int & green,
				 const int & blue)
{
  master_drawbottom( x1, y1, x2, x3, y3, red, green, blue, opacity );
  /*
  //Swap x1 and x2

  unsigned locX1( std::min( x1, x2 ) ), locX2( std::max( x1, x2 ) );

  unsigned posl=locX1*256;
  unsigned posr=locX2*256;

  unsigned cl=((x3-locX1)*256)/(y3-y1);
  unsigned cr=((x3-locX2)*256)/(y3-y1);

  for(unsigned y=y1; y<y3; y++)
    {
      line_blend(posl/256, y, posr/256, y, opacity, red, green, blue);

      posl+=cl;
      posr+=cr;
    }
  */
}

//Blend, int
void pngwriter::drawtop_blend(const unsigned & x1, const unsigned & y1,
			      const unsigned & x2,
			      const unsigned & y2, const unsigned & x3,
			      const double & opacity, const int & red,
			      const int & green, const int & blue)
{
  master_drawtop( x1, y1, x2, y2, x3, red, green, blue, opacity );
  /*
  // This swaps x2 and x3
  unsigned locX2( std::min( x2, x3 ) ), locX3( std::max( x2, x3 ) );

  unsigned posl = x1*256;
  unsigned posr = posl;

  unsigned cl=((locX2-x1)*256)/(y2-y1);
  unsigned cr=((locX3-x1)*256)/(y2-y1);

  for(unsigned y=y1; y<y2; y++)
    {
      line_blend(posl/256, y, posr/256, y, opacity, red, green, blue);
      posl+=cl;
      posr+=cr;
    }
  */
}

void pngwriter::master_triangle(const unsigned & x1, const unsigned & y1,
				const unsigned & x2, const unsigned & y2,
				const unsigned & x3, const unsigned & y3,
				const int & red, const int & green,
				const int & blue, const double & opacity)
{
  master_line(x1, y1, x2, y2, red, green, blue, opacity);
  master_line(x2, y2, x3, y3, red, green, blue, opacity);
  master_line(x3, y3, x1, y1, red, green, blue, opacity);
}

void pngwriter::triangle(const unsigned & x1, const unsigned & y1,
			 const unsigned & x2, const unsigned & y2,
			 const unsigned & x3, const unsigned & y3,
			 const int & red, const int & green, const int & blue)
{
  master_triangle( x1, y1, x2, y2, x3, y3, red, green, blue );
}

void pngwriter::triangle(const unsigned & x1, const unsigned & y1,
			 const unsigned & x2, const unsigned & y2,
			 const unsigned & x3, const unsigned & y3,
			 const pngwriter::Byte & red, const pngwriter::Byte & green,
			 const pngwriter::Byte &  blue)
			 
{
  master_triangle( x1, y1, x2, y2, x3, y3, int( red ) << 8, int( green ) << 8,
		   int( blue ) << 8 );
}

void pngwriter::triangle(const unsigned & x1, const unsigned & y1,
			 const unsigned & x2, const unsigned & y2,
			 const unsigned & x3, const unsigned & y3,
			 const double & red, const double & green,
			 const double & blue)
{
  master_triangle( x1, y1, x2, y2, x3, y3, int( red * 65535. ),
		   int( green * 65535. ), int( blue * 65535. ) );
}

void pngwriter::triangle_blend(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3, const unsigned & y3,
			       const double & opacity, const int & red,
			       const int & green, const int & blue)
{
  master_triangle( x1, y1, x2, y2, x3, y3, red, green, blue, opacity );
}

void pngwriter::triangle_blend(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3, const unsigned & y3,
			       const double & opacity, const pngwriter::Byte & red,
			       const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  master_triangle( x1, y1, x2, y2, x3, y3, int( red ) << 8, int( green ) << 8,
		   int( blue ) << 8, opacity );
}

void pngwriter::triangle_blend(const unsigned & x1, const unsigned & y1,
			       const unsigned & x2, const unsigned & y2,
			       const unsigned & x3, const unsigned & y3,
			       const double & opacity, const double & red,
			       const double & green, const double & blue)
{
  master_triangle( x1, y1, x2, y2, x3, y3, int( red * 65535. ),
		   int( green * 65535. ), int( blue * 65535. ), opacity );
}

void pngwriter::arrow( const unsigned & x1, const unsigned & y1,
		       const unsigned & x2, const unsigned & y2,
		       const unsigned & size, const double & head_angle,
		       const int & red, const int & green, const int & blue)
{

  line(x1, y1, x2, y2, red, green, blue);
  //   double th = pi_ + (head_angle)*pi_/180.0;  //degrees
  double th = pi_ + head_angle;
  double costh = cos(th);
  double sinth = sin(th);
  double t1, t2, r;   
  t1 = ((x2-x1)*costh - (y2-y1)*sinth);
  t2 = ((x2-x1)*sinth + (y2-y1)*costh);
  r = sqrt(t1*t1 + t2*t2);

  double advancex  = size*t1/r;
  double advancey  = size*t2/r;
  line(x2, y2, int(x2 + advancex), int(y2 + advancey), red, green, blue);
  t1 = (x2-x1)*costh + (y2-y1)*sinth;
  t2 =   (y2-y1)*costh - (x2-x1)*sinth;
 
  advancex  = size*t1/r;
  advancey  = size*t2/r;
  line(x2, y2, int(x2 + advancex), int(y2 + advancey), red, green, blue);
}
   
void pngwriter::filledarrow( const unsigned & x1, const unsigned & y1,
			     const unsigned & x2, const unsigned & y2,
			     const unsigned & size, const double & head_angle,
			     const int & red, const int & green,
			     const int & blue)
{
  int p1x, p2x, p3x, p1y, p2y, p3y;
   
  line(x1, y1, x2, y2, red, green, blue);
  double th = pi_ + head_angle;
  double costh = cos(th);
  double sinth = sin(th);
  double t11, t12, t21, t22, r1, r2;
  t11 = ((x2-x1)*costh - (y2-y1)*sinth);
  t21 = ((x2-x1)*sinth + (y2-y1)*costh);
  t12 = (x2-x1)*costh + (y2-y1)*sinth;
  t22 =   (y2-y1)*costh - (x2-x1)*sinth;

  r1 = sqrt(t11*t11 + t21*t21);
  r2 = sqrt(t12*t12 + t22*t22);
   
  double advancex1  = size*t11/r1;
  double advancey1  = size*t21/r1;
  double advancex2  = size*t12/r2;
  double advancey2  = size*t22/r2;

  p1x = x2;
  p1y = y2;
   
  p2x = int(x2 + advancex1);
  p2y = int(y2 + advancey1);

  p3x = int(x2 + advancex2);
  p3y = int(y2 + advancey2);

   
  filledtriangle( p1x,  p1y,  p2x,  p2y,  p3x,  p3y, red, green,  blue);
   
}

void pngwriter::arrow( const unsigned & x1, const unsigned & y1,
		       const unsigned & x2, const unsigned & y2,
		       const unsigned & size, const double & head_angle,
		       const pngwriter::Byte & red, const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  arrow( x1, y1, x2, y2, size, head_angle, int( red ) << 8, int( green ) << 8,
	 int( blue ) << 8 );
}

void pngwriter::arrow( const unsigned & x1, const unsigned & y1,
		       const unsigned & x2, const unsigned & y2,
		       const unsigned & size, const double & head_angle,
		       const double & red, const double & green,
		       const double & blue)
{
  arrow(  x1, y1, x2, y2, size,  head_angle, int( red * 65535. ),
	  int( green * 65535. ),  int( blue * 65535. ) );
}

void pngwriter::filledarrow( const unsigned & x1, const unsigned & y1,
			     const unsigned & x2, const unsigned & y2,
			     const unsigned & size, const double & head_angle,
			     const pngwriter::Byte & red, const pngwriter::Byte & green,
			     const pngwriter::Byte & blue)
{
  filledarrow( x1, y1, x2, y2, size,  head_angle,int( red * 255. ),
	       int( green * 255. ),  int( blue * 255. ) );
}

void pngwriter::filledarrow( const unsigned & x1,const unsigned & y1,
			     const unsigned & x2,const unsigned & y2,
			     const unsigned & size, const double & head_angle,
			     const double & red, const double & green,
			     const double & blue)
{
  filledarrow(  x1, y1, x2, y2, size,  head_angle, int( red * 65535. ),
		int( green * 65535. ),  int( blue * 65535. ) );
}


void pngwriter::cross( const unsigned & x, const unsigned & y,
		       const unsigned & xwidth, const unsigned & yheight,
		       const int & red, const int & green, const int & blue)
{
  line( x - xwidth / 2, y, x + xwidth / 2, y, red, green, blue);
  line( x, y - yheight / 2, x, y + yheight / 2, red, green, blue);
}

void pngwriter::maltesecross( const unsigned & x, const unsigned & y,
			      const unsigned & xwidth, const unsigned & yheight,
			      const unsigned & x_bar_height,
			      const unsigned & y_bar_width, const int & red,
			      const int & green, const int & blue)
{
  // Basic cross
  cross( x, y, xwidth, yheight, red, green, blue );
  // Bars on ends of vertical line
  line( x - y_bar_width / 2, y + yheight / 2, x + y_bar_width / 2, y + yheight / 2,
	red, green, blue);
  line( x - y_bar_width / 2, y - yheight / 2, x + y_bar_width / 2, y - yheight / 2,
	red, green, blue);
  // Bars on ends of horizontal line.
  line( x - xwidth / 2, y - x_bar_height / 2, x - xwidth / 2, y + x_bar_height / 2,
	red, green, blue);
  line( x + xwidth / 2, y - x_bar_height / 2, x + xwidth / 2, y + x_bar_height / 2,
	red, green, blue);
}

void pngwriter::cross( const unsigned & x, const unsigned & y,
		       const unsigned & xwidth, const unsigned & yheight,
		       const pngwriter::Byte & red, const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  cross( x, y, xwidth, yheight, int( red ) << 8, int( green ) << 8, int( blue ) << 8 );
}

void pngwriter::cross( const unsigned & x, const unsigned & y,
		       const unsigned & xwidth, const unsigned & yheight,
		       const double & red, const double & green,
		       const double & blue)
{
  cross( x, y, xwidth, yheight, int( 65535. * red ), int( 65535. * green ), int( 65535. * blue ) ); 
}

void pngwriter::maltesecross( const unsigned & x, const unsigned & y,
			      const unsigned & xwidth, const unsigned & yheight,
			      const unsigned & x_bar_height,
			      const unsigned & y_bar_width, const pngwriter::Byte & red,
			      const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  maltesecross( x, y, xwidth, yheight, x_bar_height, y_bar_width,
		int( red ) << 8, int( green ) << 8, int( blue ) << 8 ); 
}

void pngwriter::maltesecross( const unsigned & x, const unsigned & y,
			      const unsigned & xwidth, const unsigned & yheight,
			      const unsigned & x_bar_height,
			      const unsigned & y_bar_width, const double & red,
			      const double & green, const double & blue)
{
  maltesecross( x, y, xwidth, yheight, x_bar_height, y_bar_width,
		int( 65535. * red ), int( 65535. * green ),
		int( 65535 * blue ) ); 
}


void pngwriter::filleddiamond( const unsigned & x, const unsigned & y,
			       const int & width, const int & height,
			       const int & red, const int & green,
			       const int & blue)
{
  filledtriangle( x - width / 2, y, x, y, x, y + height / 2, red, green, blue);
  filledtriangle( x + width / 2, y, x, y, x, y + height / 2, red, green, blue);
  filledtriangle( x - width / 2, y, x, y, x, y - height / 2, red, green, blue);
  filledtriangle( x + width / 2, y, x, y, x, y - height / 2, red, green, blue);
}

void pngwriter::diamond( const unsigned & x, const unsigned & y,
			 const int & width, const int & height,
			 const int & red, const int & green, const int & blue)
{
  line( int(x - width/2.0), y, x, int(y + height/2.0), red, green, blue);
  line( int(x + width/2.0), y, x, int(y + height/2.0), red, green, blue);
  line( int(x - width/2.0), y, x, int(y - height/2.0), red, green, blue);
  line( int(x + width/2.0), y, x, int(y - height/2.0), red, green, blue);
}


void pngwriter::filleddiamond( const unsigned & x, const unsigned & y,
			       const int & width, const int & height,
			       const double & red, const double & green,
			       const double & blue)
{
  filleddiamond(  x, y,  width,  height, int( red * 65535. ),
		  int( green * 65535. ), int( blue * 65535. ) ); 
}

void pngwriter::diamond( const unsigned & x, const unsigned & y,
			 const int & width, const int & height,
			 const double & red, const double & green,
			 const double & blue)
{
  diamond(  x,  y,  width,  height, int( red * 65535. ), int( green * 65535. ),
	    int( blue * 65535. ) ); 
}

void pngwriter::filleddiamond( const unsigned & x, const unsigned & y,
			       const int & width, const int & height,
			       const pngwriter::Byte & red, const pngwriter::Byte & green, const pngwriter::Byte & blue)
{
  filleddiamond( x, y,  width,  height, int( red ) << 8, int( green ) << 8,
		 int( blue ) << 8 );
}

void pngwriter::diamond( const unsigned & x, const unsigned & y,
			 const int & width, const int & height,
			 const pngwriter::Byte & red, const pngwriter::Byte & green,
			 const pngwriter::Byte & blue)
{
  diamond( x,  y,  width,  height, int( red ) << 8, int( green ) << 8,
	   int( blue ) << 8 );
}
